diff --git a/github/agents.go b/github/agents.go new file mode 100644 index 00000000000..7bcac560221 --- /dev/null +++ b/github/agents.go @@ -0,0 +1,15 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +// AgentsService handles communication with the Agents related +// methods of the GitHub API. +// +// The Agents endpoints are only served by the 2026-03-10 API version, so these +// methods send that version explicitly instead of the client's default. +// +// GitHub API docs: https://docs.github.com/rest/agents?apiVersion=2026-03-10 +type AgentsService service diff --git a/github/agents_secrets.go b/github/agents_secrets.go new file mode 100644 index 00000000000..eecbf3785aa --- /dev/null +++ b/github/agents_secrets.go @@ -0,0 +1,322 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetRepoPublicKey gets a public key that should be used for Agents secret encryption. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-a-repository-public-key +// +//meta:operation GET /repos/{owner}/{repo}/agents/secrets/public-key +func (s *AgentsService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/agents/secrets/public-key", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var pubKey *PublicKey + resp, err := s.client.Do(req, &pubKey) + if err != nil { + return nil, resp, err + } + + return pubKey, resp, nil +} + +// GetOrgPublicKey gets a public key that should be used for Agents secret encryption. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-an-organization-public-key +// +//meta:operation GET /orgs/{org}/agents/secrets/public-key +func (s *AgentsService) GetOrgPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/public-key", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var pubKey *PublicKey + resp, err := s.client.Do(req, &pubKey) + if err != nil { + return nil, resp, err + } + + return pubKey, resp, nil +} + +// ListRepoSecrets lists all Agents secrets available in a repository +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-repository-secrets +// +//meta:operation GET /repos/{owner}/{repo}/agents/secrets +func (s *AgentsService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/agents/secrets", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// ListOrgSecrets lists all Agents secrets available in an organization +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-organization-secrets +// +//meta:operation GET /orgs/{org}/agents/secrets +func (s *AgentsService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// ListRepoOrgSecrets lists all organization Agents secrets available in a repository +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-repository-organization-secrets +// +//meta:operation GET /repos/{owner}/{repo}/agents/organization-secrets +func (s *AgentsService) ListRepoOrgSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/agents/organization-secrets", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// GetRepoSecret gets a single repository Agents secret without revealing its encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-a-repository-secret +// +//meta:operation GET /repos/{owner}/{repo}/agents/secrets/{secret_name} +func (s *AgentsService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/agents/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// GetOrgSecret gets a single organization Agents secret without revealing its encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-an-organization-secret +// +//meta:operation GET /orgs/{org}/agents/secrets/{secret_name} +func (s *AgentsService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// CreateOrUpdateRepoSecret creates or updates a repository Agents secret with an encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#create-or-update-a-repository-secret +// +//meta:operation PUT /repos/{owner}/{repo}/agents/secrets/{secret_name} +func (s *AgentsService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo, name string, body SecretRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/agents/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "PUT", u, body, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateOrUpdateOrgSecret creates or updates an organization Agents secret with an encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#create-or-update-an-organization-secret +// +//meta:operation PUT /orgs/{org}/agents/secrets/{secret_name} +func (s *AgentsService) CreateOrUpdateOrgSecret(ctx context.Context, org, name string, body SecretOrgRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "PUT", u, body, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteRepoSecret deletes an Agents secret in a repository using the secret name. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#delete-a-repository-secret +// +//meta:operation DELETE /repos/{owner}/{repo}/agents/secrets/{secret_name} +func (s *AgentsService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/agents/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteOrgSecret deletes an Agents secret in an organization using the secret name. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#delete-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/agents/secrets/{secret_name} +func (s *AgentsService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListSelectedReposForOrgSecret lists all repositories that have access to an Agents secret. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-selected-repositories-for-an-organization-secret +// +//meta:operation GET /orgs/{org}/agents/secrets/{secret_name}/repositories +func (s *AgentsService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/%v/repositories", org, name) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var result *SelectedReposList + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// SetSelectedReposForOrgSecret sets the repositories that have access to an Agents secret. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#set-selected-repositories-for-an-organization-secret +// +//meta:operation PUT /orgs/{org}/agents/secrets/{secret_name}/repositories +func (s *AgentsService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/%v/repositories", org, name) + + type repoIDs struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + } + + req, err := s.client.NewRequest(ctx, "PUT", u, repoIDs{SelectedIDs: ids}, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddSelectedRepoToOrgSecret adds a repository to an organization Agents secret. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#add-selected-repository-to-an-organization-secret +// +//meta:operation PUT /orgs/{org}/agents/secrets/{secret_name}/repositories/{repository_id} +func (s *AgentsService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/%v/repositories/%v", org, name, repoID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveSelectedRepoFromOrgSecret removes a repository from an organization Agents secret. +// +// GitHub API docs: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#remove-selected-repository-from-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/agents/secrets/{secret_name}/repositories/{repository_id} +func (s *AgentsService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/agents/secrets/%v/repositories/%v", org, name, repoID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/agents_secrets_test.go b/github/agents_secrets_test.go new file mode 100644 index 00000000000..95e90d58443 --- /dev/null +++ b/github/agents_secrets_test.go @@ -0,0 +1,607 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestAgentsService_GetRepoPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/agents/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Agents.GetRepoPublicKey(ctx, "o", "r") + if err != nil { + t.Errorf("Agents.GetRepoPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: new("1234"), Key: new("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Agents.GetRepoPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetRepoPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.GetRepoPublicKey(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.GetRepoPublicKey(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_GetRepoPublicKeyNumeric(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/agents/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{"key_id":1234,"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Agents.GetRepoPublicKey(ctx, "o", "r") + if err != nil { + t.Errorf("Agents.GetRepoPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: new("1234"), Key: new("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Agents.GetRepoPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetRepoPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.GetRepoPublicKey(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.GetRepoPublicKey(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_ListRepoSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/agents/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Agents.ListRepoSecrets(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Agents.ListRepoSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 4, + Secrets: []*Secret{ + {Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)}, + {Name: "B", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Agents.ListRepoSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListRepoSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.ListRepoSecrets(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.ListRepoSecrets(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_ListRepoOrgSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/agents/organization-secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Agents.ListRepoOrgSecrets(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Agents.ListRepoOrgSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 4, + Secrets: []*Secret{ + {Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)}, + {Name: "B", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Agents.ListRepoOrgSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListRepoOrgSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.ListRepoOrgSecrets(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.ListRepoOrgSecrets(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_GetRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/agents/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{"name":"NAME","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + secret, _, err := client.Agents.GetRepoSecret(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Agents.GetRepoSecret returned error: %v", err) + } + + want := &Secret{ + Name: "NAME", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + } + if !cmp.Equal(secret, want) { + t.Errorf("Agents.GetRepoSecret returned %+v, want %+v", secret, want) + } + + const methodName = "GetRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.GetRepoSecret(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.GetRepoSecret(ctx, "o", "r", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_CreateOrUpdateRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretRequest{ + EncryptedValue: "QIv=", + KeyID: "1234", + } + + mux.HandleFunc("/repos/o/r/agents/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testHeader(t, r, "Content-Type", "application/json") + want := SecretRequest{ + EncryptedValue: input.EncryptedValue, + KeyID: input.KeyID, + } + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Agents.CreateOrUpdateRepoSecret(ctx, "o", "r", "NAME", input) + if err != nil { + t.Errorf("Agents.CreateOrUpdateRepoSecret returned error: %v", err) + } + + const methodName = "CreateOrUpdateRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.CreateOrUpdateRepoSecret(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Agents.CreateOrUpdateRepoSecret(ctx, "o", "r", "NAME", input) + }) +} + +func TestAgentsService_DeleteRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/agents/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "X-Github-Api-Version", api20260310) + }) + + ctx := t.Context() + _, err := client.Agents.DeleteRepoSecret(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Agents.DeleteRepoSecret returned error: %v", err) + } + + const methodName = "DeleteRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.DeleteRepoSecret(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Agents.DeleteRepoSecret(ctx, "o", "r", "NAME") + }) +} + +func TestAgentsService_GetOrgPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/agents/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{"key_id":"012345678","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Agents.GetOrgPublicKey(ctx, "o") + if err != nil { + t.Errorf("Agents.GetOrgPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: new("012345678"), Key: new("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Agents.GetOrgPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetOrgPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.GetOrgPublicKey(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.GetOrgPublicKey(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_ListOrgSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/agents/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":3,"secrets":[{"name":"GIST_ID","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"private"},{"name":"DEPLOY_TOKEN","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`,"visibility":"all"},{"name":"GH_TOKEN","created_at":`+refTimeStr(1136178004)+`,"updated_at":`+refTimeStr(1136178005)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/agents/secrets/SUPER_SECRET/repositories"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Agents.ListOrgSecrets(ctx, "o", opts) + if err != nil { + t.Errorf("Agents.ListOrgSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 3, + Secrets: []*Secret{ + {Name: "GIST_ID", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001), Visibility: "private"}, + {Name: "DEPLOY_TOKEN", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003), Visibility: "all"}, + {Name: "GH_TOKEN", CreatedAt: *refTimestamp(1136178004), UpdatedAt: *refTimestamp(1136178005), Visibility: "selected", SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/agents/secrets/SUPER_SECRET/repositories"}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Agents.ListOrgSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListOrgSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.ListOrgSecrets(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.ListOrgSecrets(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_GetOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/agents/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{"name":"NAME","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/agents/secrets/SUPER_SECRET/repositories"}`) + }) + + ctx := t.Context() + secret, _, err := client.Agents.GetOrgSecret(ctx, "o", "NAME") + if err != nil { + t.Errorf("Agents.GetOrgSecret returned error: %v", err) + } + + want := &Secret{ + Name: "NAME", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + Visibility: "selected", + SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/agents/secrets/SUPER_SECRET/repositories", + } + if !cmp.Equal(secret, want) { + t.Errorf("Agents.GetOrgSecret returned %+v, want %+v", secret, want) + } + + const methodName = "GetOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.GetOrgSecret(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.GetOrgSecret(ctx, "o", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_CreateOrUpdateOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretOrgRequest{ + EncryptedValue: "QIv=", + KeyID: "1234", + Visibility: "selected", + SelectedRepositoryIDs: []int64{1296269, 1269280}, + } + + mux.HandleFunc("/orgs/o/agents/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testHeader(t, r, "Content-Type", "application/json") + want := SecretOrgRequest{ + EncryptedValue: input.EncryptedValue, + KeyID: input.KeyID, + Visibility: input.Visibility, + SelectedRepositoryIDs: input.SelectedRepositoryIDs, + } + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Agents.CreateOrUpdateOrgSecret(ctx, "o", "NAME", input) + if err != nil { + t.Errorf("Agents.CreateOrUpdateOrgSecret returned error: %v", err) + } + + const methodName = "CreateOrUpdateOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.CreateOrUpdateOrgSecret(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Agents.CreateOrUpdateOrgSecret(ctx, "o", "NAME", input) + }) +} + +func TestAgentsService_ListSelectedReposForOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/agents/secrets/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{"total_count":1,"repositories":[{"id":1}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + repos, _, err := client.Agents.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts) + if err != nil { + t.Errorf("Agents.ListSelectedReposForOrgSecret returned error: %v", err) + } + + want := &SelectedReposList{ + TotalCount: new(1), + Repositories: []*Repository{ + {ID: new(int64(1))}, + }, + } + if !cmp.Equal(repos, want) { + t.Errorf("Agents.ListSelectedReposForOrgSecret returned %+v, want %+v", repos, want) + } + + const methodName = "ListSelectedReposForOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Agents.ListSelectedReposForOrgSecret(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Agents.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAgentsService_SetSelectedReposForOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{64780797} + + mux.HandleFunc("/orgs/o/agents/secrets/NAME/repositories", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + }{ + SelectedIDs: input, + }) + }) + + ctx := t.Context() + _, err := client.Agents.SetSelectedReposForOrgSecret(ctx, "o", "NAME", input) + if err != nil { + t.Errorf("Agents.SetSelectedReposForOrgSecret returned error: %v", err) + } + + const methodName = "SetSelectedReposForOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.SetSelectedReposForOrgSecret(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Agents.SetSelectedReposForOrgSecret(ctx, "o", "NAME", input) + }) +} + +func TestAgentsService_AddSelectedRepoToOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/agents/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "X-Github-Api-Version", api20260310) + }) + + repoID := int64(1234) + ctx := t.Context() + _, err := client.Agents.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repoID) + if err != nil { + t.Errorf("Agents.AddSelectedRepoToOrgSecret returned error: %v", err) + } + + const methodName = "AddSelectedRepoToOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", 0) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.AddSelectedRepoToOrgSecret(ctx, "\n", "\n", repoID) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Agents.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repoID) + }) +} + +func TestAgentsService_RemoveSelectedRepoFromOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/agents/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "X-Github-Api-Version", api20260310) + }) + + repoID := int64(1234) + ctx := t.Context() + _, err := client.Agents.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repoID) + if err != nil { + t.Errorf("Agents.RemoveSelectedRepoFromOrgSecret returned error: %v", err) + } + + const methodName = "RemoveSelectedRepoFromOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", 0) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.RemoveSelectedRepoFromOrgSecret(ctx, "\n", "\n", repoID) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Agents.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repoID) + }) +} + +func TestAgentsService_DeleteOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/agents/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "X-Github-Api-Version", api20260310) + }) + + ctx := t.Context() + _, err := client.Agents.DeleteOrgSecret(ctx, "o", "NAME") + if err != nil { + t.Errorf("Agents.DeleteOrgSecret returned error: %v", err) + } + + const methodName = "DeleteOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Agents.DeleteOrgSecret(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Agents.DeleteOrgSecret(ctx, "o", "NAME") + }) +} diff --git a/github/github-iterators.go b/github/github-iterators.go index 22a485af004..fd2c5baca24 100644 --- a/github/github-iterators.go +++ b/github/github-iterators.go @@ -1568,6 +1568,146 @@ func (s *AgentTasksService) ListByRepoIter(ctx context.Context, owner string, re } } +// ListOrgSecretsIter returns an iterator that paginates through all results of ListOrgSecrets. +func (s *AgentsService) ListOrgSecretsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = new(*opts) + } + + for { + results, resp, err := s.ListOrgSecrets(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepoOrgSecretsIter returns an iterator that paginates through all results of ListRepoOrgSecrets. +func (s *AgentsService) ListRepoOrgSecretsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = new(*opts) + } + + for { + results, resp, err := s.ListRepoOrgSecrets(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepoSecretsIter returns an iterator that paginates through all results of ListRepoSecrets. +func (s *AgentsService) ListRepoSecretsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = new(*opts) + } + + for { + results, resp, err := s.ListRepoSecrets(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListSelectedReposForOrgSecretIter returns an iterator that paginates through all results of ListSelectedReposForOrgSecret. +func (s *AgentsService) ListSelectedReposForOrgSecretIter(ctx context.Context, org string, name string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = new(*opts) + } + + for { + results, resp, err := s.ListSelectedReposForOrgSecret(ctx, org, name, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + // ListHookDeliveriesIter returns an iterator that paginates through all results of ListHookDeliveries. func (s *AppsService) ListHookDeliveriesIter(ctx context.Context, opts *ListCursorOptions) iter.Seq2[*HookDelivery, error] { return func(yield func(*HookDelivery, error) bool) { diff --git a/github/github-iterators_test.go b/github/github-iterators_test.go index e590fba8703..f32704ad3af 100644 --- a/github/github-iterators_test.go +++ b/github/github-iterators_test.go @@ -3327,6 +3327,294 @@ func TestAgentTasksService_ListByRepoIter(t *testing.T) { } } +func TestAgentsService_ListOrgSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Agents.ListOrgSecretsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Agents.ListOrgSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Agents.ListOrgSecretsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Agents.ListOrgSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Agents.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Agents.ListOrgSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Agents.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Agents.ListOrgSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAgentsService_ListRepoOrgSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Agents.ListRepoOrgSecretsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Agents.ListRepoOrgSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Agents.ListRepoOrgSecretsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Agents.ListRepoOrgSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Agents.ListRepoOrgSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Agents.ListRepoOrgSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Agents.ListRepoOrgSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Agents.ListRepoOrgSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAgentsService_ListRepoSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Agents.ListRepoSecretsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Agents.ListRepoSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Agents.ListRepoSecretsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Agents.ListRepoSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Agents.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Agents.ListRepoSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Agents.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Agents.ListRepoSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAgentsService_ListSelectedReposForOrgSecretIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Agents.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Agents.ListSelectedReposForOrgSecretIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Agents.ListSelectedReposForOrgSecretIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Agents.ListSelectedReposForOrgSecretIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Agents.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Agents.ListSelectedReposForOrgSecretIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Agents.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Agents.ListSelectedReposForOrgSecretIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + func TestAppsService_ListHookDeliveriesIter(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/github.go b/github/github.go index 91569340f16..5ab7a26be85 100644 --- a/github/github.go +++ b/github/github.go @@ -209,6 +209,7 @@ type Client struct { Activity *ActivityService Admin *AdminService AgentTasks *AgentTasksService + Agents *AgentsService Apps *AppsService Authorizations *AuthorizationsService Billing *BillingService @@ -663,6 +664,7 @@ func newClient(opts clientOptions) (*Client, error) { c.Activity = (*ActivityService)(&c.common) c.Admin = (*AdminService)(&c.common) c.AgentTasks = (*AgentTasksService)(&c.common) + c.Agents = (*AgentsService)(&c.common) c.Apps = (*AppsService)(&c.common) c.Authorizations = (*AuthorizationsService)(&c.common) c.Billing = (*BillingService)(&c.common) diff --git a/openapi_operations.yaml b/openapi_operations.yaml index 5e985b15ac9..2d419236648 100644 --- a/openapi_operations.yaml +++ b/openapi_operations.yaml @@ -79,6 +79,36 @@ operations: operation_overrides: - name: GET /meta documentation_url: https://docs.github.com/rest/meta/meta#get-github-meta-information + - name: GET /orgs/{org}/agents/secrets + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-organization-secrets + - name: GET /orgs/{org}/agents/secrets/public-key + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-an-organization-public-key + - name: DELETE /orgs/{org}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#delete-an-organization-secret + - name: GET /orgs/{org}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-an-organization-secret + - name: PUT /orgs/{org}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#create-or-update-an-organization-secret + - name: GET /orgs/{org}/agents/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-selected-repositories-for-an-organization-secret + - name: PUT /orgs/{org}/agents/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#set-selected-repositories-for-an-organization-secret + - name: DELETE /orgs/{org}/agents/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#remove-selected-repository-from-an-organization-secret + - name: PUT /orgs/{org}/agents/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#add-selected-repository-to-an-organization-secret + - name: GET /repos/{owner}/{repo}/agents/organization-secrets + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-repository-organization-secrets + - name: GET /repos/{owner}/{repo}/agents/secrets + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#list-repository-secrets + - name: GET /repos/{owner}/{repo}/agents/secrets/public-key + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-a-repository-public-key + - name: DELETE /repos/{owner}/{repo}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#delete-a-repository-secret + - name: GET /repos/{owner}/{repo}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#get-a-repository-secret + - name: PUT /repos/{owner}/{repo}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets?apiVersion=2026-03-10#create-or-update-a-repository-secret - name: GET /orgs/{org}/private-registries documentation_url: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#list-private-registries-for-an-organization - name: POST /orgs/{org}/private-registries