diff --git a/github/code_quality.go b/github/code_quality.go new file mode 100644 index 00000000000..c3315c0724d --- /dev/null +++ b/github/code_quality.go @@ -0,0 +1,89 @@ +// 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" +) + +// CodeQualityService handles communication with the code quality related +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28 +type CodeQualityService service + +// CodeQualitySetupConfiguration represents a code quality setup configuration for a repository. +type CodeQualitySetupConfiguration struct { + State *string `json:"state,omitempty"` + Languages []string `json:"languages,omitempty"` + RunnerType *string `json:"runner_type,omitempty"` + RunnerLabel *string `json:"runner_label,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Schedule *string `json:"schedule,omitempty"` +} + +// UpdateCodeQualitySetupOptions specifies parameters to the +// CodeQualityService.UpdateSetup method. +type UpdateCodeQualitySetupOptions struct { + State *string `json:"state,omitempty"` + RunnerType *string `json:"runner_type,omitempty"` + RunnerLabel *string `json:"runner_label,omitempty"` + Languages []string `json:"languages,omitempty"` +} + +// UpdateCodeQualitySetupResponse represents a response from updating a code quality setup configuration. +type UpdateCodeQualitySetupResponse struct { + RunID *int64 `json:"run_id,omitempty"` + RunURL *string `json:"run_url,omitempty"` +} + +// GetSetup gets a code quality setup configuration for a repository. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28#get-a-code-quality-setup-configuration +// +//meta:operation GET /repos/{owner}/{repo}/code-quality/setup +func (s *CodeQualityService) GetSetup(ctx context.Context, owner, repo string) (*CodeQualitySetupConfiguration, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-quality/setup", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var cfg *CodeQualitySetupConfiguration + resp, err := s.client.Do(req, &cfg) + if err != nil { + return nil, resp, err + } + + return cfg, resp, nil +} + +// UpdateSetup updates a code quality setup configuration for a repository. +// +// This method might return an AcceptedError and a status code of 202. This is because this is the status that GitHub +// returns to signify that it has now scheduled the update in a background task. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28#update-a-code-quality-setup-configuration +// +//meta:operation PATCH /repos/{owner}/{repo}/code-quality/setup +func (s *CodeQualityService) UpdateSetup(ctx context.Context, owner, repo string, opts *UpdateCodeQualitySetupOptions) (*UpdateCodeQualitySetupResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-quality/setup", owner, repo) + + req, err := s.client.NewRequest(ctx, "PATCH", u, opts) + if err != nil { + return nil, nil, err + } + + var result *UpdateCodeQualitySetupResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} diff --git a/github/code_quality_test.go b/github/code_quality_test.go new file mode 100644 index 00000000000..1a16445613f --- /dev/null +++ b/github/code_quality_test.go @@ -0,0 +1,185 @@ +// 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" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestCodeQualityService_GetSetup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "state": "configured", + "languages": ["javascript-typescript", "python"], + "runner_type": "standard", + "runner_label": null, + "updated_at": "2026-01-01T00:00:00Z", + "schedule": "weekly" + }`) + }) + + ctx := t.Context() + cfg, _, err := client.CodeQuality.GetSetup(ctx, "o", "r") + if err != nil { + t.Fatalf("CodeQuality.GetSetup returned error: %v", err) + } + + want := &CodeQualitySetupConfiguration{ + State: Ptr("configured"), + Languages: []string{"javascript-typescript", "python"}, + RunnerType: Ptr("standard"), + UpdatedAt: &Timestamp{time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC)}, + Schedule: Ptr("weekly"), + } + if diff := cmp.Diff(want, cfg); diff != "" { + t.Errorf("CodeQuality.GetSetup mismatch (-want +got):\n%v", diff) + } + + const methodName = "GetSetup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeQuality.GetSetup(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeQuality.GetSetup(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeQualityService_UpdateSetup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &UpdateCodeQualitySetupOptions{ + State: Ptr("configured"), + Languages: []string{"javascript-typescript", "python", "ruby"}, + } + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "run_id": 42, + "run_url": "https://api.github.com/repos/octocat/hello-world/actions/runs/42" + }`) + }) + + ctx := t.Context() + result, _, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", input) + if err != nil { + t.Fatalf("CodeQuality.UpdateSetup returned error: %v", err) + } + + want := &UpdateCodeQualitySetupResponse{ + RunID: Ptr(int64(42)), + RunURL: Ptr("https://api.github.com/repos/octocat/hello-world/actions/runs/42"), + } + if diff := cmp.Diff(want, result); diff != "" { + t.Errorf("CodeQuality.UpdateSetup mismatch (-want +got):\n%v", diff) + } + + const methodName = "UpdateSetup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeQuality.UpdateSetup(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeQualityService_UpdateSetup_withRunnerLabel(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &UpdateCodeQualitySetupOptions{ + State: Ptr("configured"), + RunnerType: Ptr("labeled"), + RunnerLabel: Ptr("my-runner"), + Languages: []string{"go", "python"}, + } + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "run_id": 99, + "run_url": "https://api.github.com/repos/octocat/hello-world/actions/runs/99" + }`) + }) + + ctx := t.Context() + result, _, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", input) + if err != nil { + t.Fatalf("CodeQuality.UpdateSetup returned error: %v", err) + } + + want := &UpdateCodeQualitySetupResponse{ + RunID: Ptr(int64(99)), + RunURL: Ptr("https://api.github.com/repos/octocat/hello-world/actions/runs/99"), + } + if diff := cmp.Diff(want, result); diff != "" { + t.Errorf("CodeQuality.UpdateSetup mismatch (-want +got):\n%v", diff) + } +} + +func TestCodeQualityService_UpdateSetup_notConfigured(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &UpdateCodeQualitySetupOptions{ + State: Ptr("not-configured"), + } + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{}`) + }) + + ctx := t.Context() + _, _, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", input) + if err != nil { + t.Fatalf("CodeQuality.UpdateSetup returned error: %v", err) + } +} + +func TestCodeQualityService_GetSetup_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.CodeQuality.GetSetup(ctx, "%", "r") + testURLParseError(t, err) +} + +func TestCodeQualityService_UpdateSetup_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.CodeQuality.UpdateSetup(ctx, "%", "r", nil) + testURLParseError(t, err) +} diff --git a/github/github-accessors.go b/github/github-accessors.go index 5cb0f76f730..85ec1c948d6 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -4790,6 +4790,54 @@ func (c *CodeQLDatabase) GetURL() string { return *c.URL } +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (c *CodeQualitySetupConfiguration) GetLanguages() []string { + if c == nil || c.Languages == nil { + return nil + } + return c.Languages +} + +// GetRunnerLabel returns the RunnerLabel field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetRunnerLabel() string { + if c == nil || c.RunnerLabel == nil { + return "" + } + return *c.RunnerLabel +} + +// GetRunnerType returns the RunnerType field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetRunnerType() string { + if c == nil || c.RunnerType == nil { + return "" + } + return *c.RunnerType +} + +// GetSchedule returns the Schedule field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetSchedule() string { + if c == nil || c.Schedule == nil { + return "" + } + return *c.Schedule +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetState() string { + if c == nil || c.State == nil { + return "" + } + return *c.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} + } + return *c.UpdatedAt +} + // GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. func (c *CodeResult) GetHTMLURL() string { if c == nil || c.HTMLURL == nil { @@ -40998,6 +41046,54 @@ func (u *UpdateCheckRunOptions) GetStatus() string { return *u.Status } +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (u *UpdateCodeQualitySetupOptions) GetLanguages() []string { + if u == nil || u.Languages == nil { + return nil + } + return u.Languages +} + +// GetRunnerLabel returns the RunnerLabel field if it's non-nil, zero value otherwise. +func (u *UpdateCodeQualitySetupOptions) GetRunnerLabel() string { + if u == nil || u.RunnerLabel == nil { + return "" + } + return *u.RunnerLabel +} + +// GetRunnerType returns the RunnerType field if it's non-nil, zero value otherwise. +func (u *UpdateCodeQualitySetupOptions) GetRunnerType() string { + if u == nil || u.RunnerType == nil { + return "" + } + return *u.RunnerType +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (u *UpdateCodeQualitySetupOptions) GetState() string { + if u == nil || u.State == nil { + return "" + } + return *u.State +} + +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (u *UpdateCodeQualitySetupResponse) GetRunID() int64 { + if u == nil || u.RunID == nil { + return 0 + } + return *u.RunID +} + +// GetRunURL returns the RunURL field if it's non-nil, zero value otherwise. +func (u *UpdateCodeQualitySetupResponse) GetRunURL() string { + if u == nil || u.RunURL == nil { + return "" + } + return *u.RunURL +} + // GetMachine returns the Machine field if it's non-nil, zero value otherwise. func (u *UpdateCodespaceOptions) GetMachine() string { if u == nil || u.Machine == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 8ef40f76f75..9d1ca51b911 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -6114,6 +6114,72 @@ func TestCodeQLDatabase_GetURL(tt *testing.T) { c.GetURL() } +func TestCodeQualitySetupConfiguration_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CodeQualitySetupConfiguration{Languages: zeroValue} + c.GetLanguages() + c = &CodeQualitySetupConfiguration{} + c.GetLanguages() + c = nil + c.GetLanguages() +} + +func TestCodeQualitySetupConfiguration_GetRunnerLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{RunnerLabel: &zeroValue} + c.GetRunnerLabel() + c = &CodeQualitySetupConfiguration{} + c.GetRunnerLabel() + c = nil + c.GetRunnerLabel() +} + +func TestCodeQualitySetupConfiguration_GetRunnerType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{RunnerType: &zeroValue} + c.GetRunnerType() + c = &CodeQualitySetupConfiguration{} + c.GetRunnerType() + c = nil + c.GetRunnerType() +} + +func TestCodeQualitySetupConfiguration_GetSchedule(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{Schedule: &zeroValue} + c.GetSchedule() + c = &CodeQualitySetupConfiguration{} + c.GetSchedule() + c = nil + c.GetSchedule() +} + +func TestCodeQualitySetupConfiguration_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{State: &zeroValue} + c.GetState() + c = &CodeQualitySetupConfiguration{} + c.GetState() + c = nil + c.GetState() +} + +func TestCodeQualitySetupConfiguration_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodeQualitySetupConfiguration{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CodeQualitySetupConfiguration{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + func TestCodeResult_GetHTMLURL(tt *testing.T) { tt.Parallel() var zeroValue string @@ -51535,6 +51601,72 @@ func TestUpdateCheckRunOptions_GetStatus(tt *testing.T) { u.GetStatus() } +func TestUpdateCodeQualitySetupOptions_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateCodeQualitySetupOptions{Languages: zeroValue} + u.GetLanguages() + u = &UpdateCodeQualitySetupOptions{} + u.GetLanguages() + u = nil + u.GetLanguages() +} + +func TestUpdateCodeQualitySetupOptions_GetRunnerLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCodeQualitySetupOptions{RunnerLabel: &zeroValue} + u.GetRunnerLabel() + u = &UpdateCodeQualitySetupOptions{} + u.GetRunnerLabel() + u = nil + u.GetRunnerLabel() +} + +func TestUpdateCodeQualitySetupOptions_GetRunnerType(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCodeQualitySetupOptions{RunnerType: &zeroValue} + u.GetRunnerType() + u = &UpdateCodeQualitySetupOptions{} + u.GetRunnerType() + u = nil + u.GetRunnerType() +} + +func TestUpdateCodeQualitySetupOptions_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCodeQualitySetupOptions{State: &zeroValue} + u.GetState() + u = &UpdateCodeQualitySetupOptions{} + u.GetState() + u = nil + u.GetState() +} + +func TestUpdateCodeQualitySetupResponse_GetRunID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UpdateCodeQualitySetupResponse{RunID: &zeroValue} + u.GetRunID() + u = &UpdateCodeQualitySetupResponse{} + u.GetRunID() + u = nil + u.GetRunID() +} + +func TestUpdateCodeQualitySetupResponse_GetRunURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCodeQualitySetupResponse{RunURL: &zeroValue} + u.GetRunURL() + u = &UpdateCodeQualitySetupResponse{} + u.GetRunURL() + u = nil + u.GetRunURL() +} + func TestUpdateCodespaceOptions_GetMachine(tt *testing.T) { tt.Parallel() var zeroValue string diff --git a/github/github.go b/github/github.go index 17aeb25661a..289abcc49fe 100644 --- a/github/github.go +++ b/github/github.go @@ -213,6 +213,7 @@ type Client struct { Billing *BillingService Checks *ChecksService Classroom *ClassroomService + CodeQuality *CodeQualityService CodeScanning *CodeScanningService CodesOfConduct *CodesOfConductService Codespaces *CodespacesService @@ -665,6 +666,7 @@ func newClient(opts clientOptions) (*Client, error) { c.Billing = (*BillingService)(&c.common) c.Checks = (*ChecksService)(&c.common) c.Classroom = (*ClassroomService)(&c.common) + c.CodeQuality = (*CodeQualityService)(&c.common) c.CodeScanning = (*CodeScanningService)(&c.common) c.Codespaces = (*CodespacesService)(&c.common) c.CodesOfConduct = (*CodesOfConductService)(&c.common)