Repository: google/go-github
Pull request #2408 (2022-07-08 19:03:43)

feat: add GetCodeownersErrors to RepositoriesService (#2405)
This PR adds a new function `GetCodeownersErrors` to the `RepositoriesService` struct.
It adds support for the endpoint https://docs.github.com/en/rest/repos/repos#list-codeowners-errors as described in #2405 

codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/2408?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#2408](https://codecov.io/gh/google/go-github/pull/2408?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (9185955) into [master](https://codecov.io/gh/google/go-github/commit/fd22ee93382e50d36592170f63c6a08d7c35ed8d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (fd22ee9) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #2408   +/-   ##
=======================================
  Coverage   98.06%   98.06%           
=======================================
  Files         119      120    +1     
  Lines       10546    10558   +12     
=======================================
+ Hits        10342    10354   +12     
  Misses        140      140           
  Partials       64       64           
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/2408?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) | Coverage Δ | |
|---|---|---|
| [github/repos\_codeowners.go](https://codecov.io/gh/google/go-github/pull/2408/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL3JlcG9zX2NvZGVvd25lcnMuZ28=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/2408?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/google/go-github/pull/2408?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Last update [fd22ee9...9185955](https://codecov.io/gh/google/go-github/pull/2408?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).


Patch:
diff --git a/github/github-accessors.go b/github/github-accessors.go
index da146bcb..334ff395 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -1030,6 +1030,14 @@ func (a *AuditEntry) GetRepositoryPublic() bool {
 	return *a.RepositoryPublic
 }
 
+// GetRunAttempt returns the RunAttempt field if it's non-nil, zero value otherwise.
+func (a *AuditEntry) GetRunAttempt() int64 {
+	if a == nil || a.RunAttempt == nil {
+		return 0
+	}
+	return *a.RunAttempt
+}
+
 // GetRunnerGroupID returns the RunnerGroupID field if it's non-nil, zero value otherwise.
 func (a *AuditEntry) GetRunnerGroupID() int64 {
 	if a == nil || a.RunnerGroupID == nil {
@@ -2286,6 +2294,14 @@ func (c *CodeOfConduct) GetURL() string {
 	return *c.URL
 }
 
+// GetSuggestion returns the Suggestion field if it's non-nil, zero value otherwise.
+func (c *CodeownersError) GetSuggestion() string {
+	if c == nil || c.Suggestion == nil {
+		return ""
+	}
+	return *c.Suggestion
+}
+
 // GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
 func (c *CodeResult) GetHTMLURL() string {
 	if c == nil || c.HTMLURL == nil {
@@ -10790,12 +10806,12 @@ func (p *PagesUpdate) GetPublic() bool {
 	return *p.Public
 }
 
-// GetSource returns the Source field if it's non-nil, zero value otherwise.
-func (p *PagesUpdate) GetSource() string {
-	if p == nil || p.Source == nil {
-		return ""
+// GetSource returns the Source field.
+func (p *PagesUpdate) GetSource() *PagesSource {
+	if p == nil {
+		return nil
 	}
-	return *p.Source
+	return p.Source
 }
 
 // GetHook returns the Hook field.
diff --git a/github/orgs_audit_log.go b/github/orgs_audit_log.go
index 52bacfed..be5fd24d 100644
--- a/github/orgs_audit_log.go
+++ b/github/orgs_audit_log.go
@@ -70,6 +70,7 @@ type AuditEntry struct {
 	Repo                  *string     `json:"repo,omitempty"`
 	Repository            *string     `json:"repository,omitempty"`
 	RepositoryPublic      *bool       `json:"repository_public,omitempty"`
+	RunAttempt            *int64      `json:"run_attempt,omitempty"`
 	RunnerGroupID         *int64      `json:"runner_group_id,omitempty"`
 	RunnerGroupName       *string     `json:"runner_group_name,omitempty"`
 	RunnerID              *int64      `json:"runner_id,omitempty"`
diff --git a/github/repos_codeowners.go b/github/repos_codeowners.go
new file mode 100644
index 00000000..835d56e1
--- /dev/null
+++ b/github/repos_codeowners.go
@@ -0,0 +1,46 @@
+// Copyright 2022 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"
+)
+
+// CodeownersErrors represents a list of syntax errors detected in the CODEOWNERS file.
+type CodeownersErrors struct {
+	Errors []*CodeownersError `json:"errors"`
+}
+
+// CodeownersError represents a syntax error detected in the CODEOWNERS file.
+type CodeownersError struct {
+	Line       int     `json:"line"`
+	Column     int     `json:"column"`
+	Kind       string  `json:"kind"`
+	Source     string  `json:"source"`
+	Suggestion *string `json:"suggestion,omitempty"`
+	Message    string  `json:"message"`
+	Path       string  `json:"path"`
+}
+
+// GetCodeownersErrors lists any syntax errors that are detected in the CODEOWNERS file.
+//
+// GitHub API docs: https://docs.github.com/en/rest/repos/repos#list-codeowners-errors
+func (s *RepositoriesService) GetCodeownersErrors(ctx context.Context, owner, repo string) (*CodeownersErrors, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/codeowners/errors", owner, repo)
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	codeownersErrors := &CodeownersErrors{}
+	resp, err := s.client.Do(ctx, req, codeownersErrors)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return codeownersErrors, resp, nil
+}
diff --git a/github/repos_pages.go b/github/repos_pages.go
index 9b864eb0..737cec0f 100644
--- a/github/repos_pages.go
+++ b/github/repos_pages.go
@@ -93,8 +93,10 @@ type PagesUpdate struct {
 	// Leaving CNAME empty will remove the custom domain.
 	CNAME *string `json:"cname"`
 	// Source must include the branch name, and may optionally specify the subdirectory "/docs".
-	// Possible values are: "gh-pages", "master", and "master /docs".
-	Source *string `json:"source,omitempty"`
+	// Possible values for Source.Branch are usually "gh-pages", "main", and "master",
+	// or any other existing branch name.
+	// Possible values for Source.Path are: "/", and "/docs".
+	Source *PagesSource `json:"source,omitempty"`
 	// Public configures access controls for the site.
 	// If "true", the site will be accessible to anyone on the internet. If "false",
 	// the site will be accessible to anyone with read access to the repository that


Test patch:
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 5d30ec96..4339a879 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -1216,6 +1216,16 @@ func TestAuditEntry_GetRepositoryPublic(tt *testing.T) {
 	a.GetRepositoryPublic()
 }
 
+func TestAuditEntry_GetRunAttempt(tt *testing.T) {
+	var zeroValue int64
+	a := &AuditEntry{RunAttempt: &zeroValue}
+	a.GetRunAttempt()
+	a = &AuditEntry{}
+	a.GetRunAttempt()
+	a = nil
+	a.GetRunAttempt()
+}
+
 func TestAuditEntry_GetRunnerGroupID(tt *testing.T) {
 	var zeroValue int64
 	a := &AuditEntry{RunnerGroupID: &zeroValue}
@@ -2699,6 +2709,16 @@ func TestCodeOfConduct_GetURL(tt *testing.T) {
 	c.GetURL()
 }
 
+func TestCodeownersError_GetSuggestion(tt *testing.T) {
+	var zeroValue string
+	c := &CodeownersError{Suggestion: &zeroValue}
+	c.GetSuggestion()
+	c = &CodeownersError{}
+	c.GetSuggestion()
+	c = nil
+	c.GetSuggestion()
+}
+
 func TestCodeResult_GetHTMLURL(tt *testing.T) {
 	var zeroValue string
 	c := &CodeResult{HTMLURL: &zeroValue}
@@ -12643,10 +12663,7 @@ func TestPagesUpdate_GetPublic(tt *testing.T) {
 }
 
 func TestPagesUpdate_GetSource(tt *testing.T) {
-	var zeroValue string
-	p := &PagesUpdate{Source: &zeroValue}
-	p.GetSource()
-	p = &PagesUpdate{}
+	p := &PagesUpdate{}
 	p.GetSource()
 	p = nil
 	p.GetSource()
diff --git a/github/orgs_audit_log_test.go b/github/orgs_audit_log_test.go
index c2beca2b..1f060072 100644
--- a/github/orgs_audit_log_test.go
+++ b/github/orgs_audit_log_test.go
@@ -207,6 +207,7 @@ func TestAuditEntry_Marshal(t *testing.T) {
 		Repo:                  String("r"),
 		Repository:            String("repo"),
 		RepositoryPublic:      Bool(false),
+		RunAttempt:            Int64(1),
 		RunnerGroupID:         Int64(1),
 		RunnerGroupName:       String("rgn"),
 		RunnerID:              Int64(1),
@@ -275,6 +276,7 @@ func TestAuditEntry_Marshal(t *testing.T) {
 		"repo": "r",
 		"repository": "repo",
 		"repository_public": false,
+		"run_attempt": 1,
 		"runner_group_id": 1,
 		"runner_group_name": "rgn",
 		"runner_id": 1,
diff --git a/github/repos_codeowners_test.go b/github/repos_codeowners_test.go
new file mode 100644
index 00000000..62c58c5b
--- /dev/null
+++ b/github/repos_codeowners_test.go
@@ -0,0 +1,110 @@
+// Copyright 2022 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"
+	"net/http"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestRepositoriesService_GetCodeownersErrors(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/codeowners/errors", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeV3)
+		fmt.Fprint(w, `{
+		  "errors": [
+			{
+			  "line": 1,
+			  "column": 1,
+			  "kind": "Invalid pattern",
+			  "source": "***/*.rb @monalisa",
+			  "suggestion": "Did you mean **/*.rb?",
+			  "message": "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+			  "path": ".github/CODEOWNERS"
+			}
+		  ]
+		}
+	`)
+	})
+
+	ctx := context.Background()
+	codeownersErrors, _, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r")
+	if err != nil {
+		t.Errorf("Repositories.GetCodeownersErrors returned error: %v", err)
+	}
+
+	want := &CodeownersErrors{
+		Errors: []*CodeownersError{
+			{
+				Line:       1,
+				Column:     1,
+				Kind:       "Invalid pattern",
+				Source:     "***/*.rb @monalisa",
+				Suggestion: String("Did you mean **/*.rb?"),
+				Message:    "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+				Path:       ".github/CODEOWNERS",
+			},
+		},
+	}
+	if !cmp.Equal(codeownersErrors, want) {
+		t.Errorf("Repositories.GetCodeownersErrors returned %+v, want %+v", codeownersErrors, want)
+	}
+
+	const methodName = "GetCodeownersErrors"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.GetCodeownersErrors(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestCodeownersErrors_Marshal(t *testing.T) {
+	testJSONMarshal(t, &CodeownersErrors{}, "{}")
+
+	u := &CodeownersErrors{
+		Errors: []*CodeownersError{
+			{
+				Line:       1,
+				Column:     1,
+				Kind:       "Invalid pattern",
+				Source:     "***/*.rb @monalisa",
+				Suggestion: String("Did you mean **/*.rb?"),
+				Message:    "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+				Path:       ".github/CODEOWNERS",
+			},
+		},
+	}
+
+	want := `{
+	  "errors": [
+		{
+		  "line": 1,
+		  "column": 1,
+		  "kind": "Invalid pattern",
+		  "source": "***/*.rb @monalisa",
+		  "suggestion": "Did you mean **/*.rb?",
+		  "message": "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+		  "path": ".github/CODEOWNERS"
+		}
+	  ]
+	}
+`
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_pages_test.go b/github/repos_pages_test.go
index 46bffcff..ed17e735 100644
--- a/github/repos_pages_test.go
+++ b/github/repos_pages_test.go
@@ -76,7 +76,7 @@ func TestRepositoriesService_UpdatePages(t *testing.T) {
 
 	input := &PagesUpdate{
 		CNAME:  String("www.my-domain.com"),
-		Source: String("gh-pages"),
+		Source: &PagesSource{Branch: String("gh-pages")},
 	}
 
 	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
@@ -84,12 +84,12 @@ func TestRepositoriesService_UpdatePages(t *testing.T) {
 		json.NewDecoder(r.Body).Decode(v)
 
 		testMethod(t, r, "PUT")
-		want := &PagesUpdate{CNAME: String("www.my-domain.com"), Source: String("gh-pages")}
+		want := &PagesUpdate{CNAME: String("www.my-domain.com"), Source: &PagesSource{Branch: String("gh-pages")}}
 		if !cmp.Equal(v, want) {
 			t.Errorf("Request body = %+v, want %+v", v, want)
 		}
 
-		fmt.Fprint(w, `{"cname":"www.my-domain.com","source":"gh-pages"}`)
+		fmt.Fprint(w, `{"cname":"www.my-domain.com","source":{"branch":"gh-pages"}}`)
 	})
 
 	ctx := context.Background()
@@ -114,7 +114,7 @@ func TestRepositoriesService_UpdatePages_NullCNAME(t *testing.T) {
 	defer teardown()
 
 	input := &PagesUpdate{
-		Source: String("gh-pages"),
+		Source: &PagesSource{Branch: String("gh-pages")},
 	}
 
 	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
@@ -123,12 +123,12 @@ func TestRepositoriesService_UpdatePages_NullCNAME(t *testing.T) {
 			t.Fatalf("unable to read body: %v", err)
 		}
 
-		want := []byte(`{"cname":null,"source":"gh-pages"}` + "\n")
+		want := []byte(`{"cname":null,"source":{"branch":"gh-pages"}}` + "\n")
 		if !bytes.Equal(got, want) {
 			t.Errorf("Request body = %+v, want %+v", got, want)
 		}
 
-		fmt.Fprint(w, `{"cname":null,"source":"gh-pages"}`)
+		fmt.Fprint(w, `{"cname":null,"source":{"branch":"gh-pages"}}`)
 	})
 
 	ctx := context.Background()
@@ -393,12 +393,12 @@ func TestPagesUpdate_Marshal(t *testing.T) {
 
 	u := &PagesUpdate{
 		CNAME:  String("cname"),
-		Source: String("src"),
+		Source: &PagesSource{Path: String("src")},
 	}
 
 	want := `{
 		"cname": "cname",
-		"source": "src"
+		"source": { "path": "src" }
 	}`
 
 	testJSONMarshal(t, u, want)