Repository: google/go-github
Pull request #2046 (2021-08-18 13:30:21)

Add support for autolinks
Fixes: #2044.

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

[![Impacted file tree graph](https://codecov.io/gh/google/go-github/pull/2046/graphs/tree.svg?width=650&height=150&src=pr&token=EgYjGjZlI0&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)](https://codecov.io/gh/google/go-github/pull/2046?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)

```diff
@@            Coverage Diff             @@
##           master    #2046      +/-   ##
==========================================
+ Coverage   97.87%   97.88%   +0.01%     
==========================================
  Files         107      108       +1     
  Lines        6907     6942      +35     
==========================================
+ Hits         6760     6795      +35     
  Misses         81       81              
  Partials       66       66              
```


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

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/2046?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/2046?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Last update [39e5418...c6e087b](https://codecov.io/gh/google/go-github/pull/2046?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/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..d4960c13
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,18 @@
+version: 2
+updates:
+- package-ecosystem: gomod
+  directory: /
+  schedule:
+    interval: weekly
+- package-ecosystem: gomod
+  directory: scrape
+  schedule:
+    interval: weekly
+- package-ecosystem: gomod
+  directory: update-urls
+  schedule:
+    interval: weekly
+- package-ecosystem: github-actions
+  directory: /
+  schedule:
+    interval: weekly
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
new file mode 100644
index 00000000..5f35fac3
--- /dev/null
+++ b/.github/workflows/linter.yml
@@ -0,0 +1,19 @@
+on: [push, pull_request]
+name: linter
+
+jobs:
+  lint:
+    strategy:
+      matrix:
+        go-version: [1.x]
+        platform: [ubuntu-latest]
+    runs-on: ${{ matrix.platform }}
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: golangci-lint
+      uses: golangci/golangci-lint-action@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018 #v2.5.2
+      with:
+        version: v1.41
+        args: --verbose
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 00000000..afd9675f
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,32 @@
+linters:
+  # TODO: fix errors so that all of the linters below pass.
+  # The linters that are commented out, as well as those explicitly disabled,
+  # are currently failing.  We should fix those failures or define exclusion
+  # rules, and then enable those linters.
+  enable:
+    - dogsled
+    - dupl
+    - gofmt
+    - goimports
+  # - gosec
+    - misspell
+    - nakedret
+  # - stylecheck
+  # - unconvert
+  # - unparam
+  # - whitespace
+  disable:
+    - errcheck
+    - gosimple
+    - staticcheck
+    - ineffassign
+    - unused
+issues: 
+  exclude-rules:
+  - linters:
+    - dogsled
+    text:  "declaration has 3 blank identifiers"
+    path: _test\.go
+  - linters:
+    - dupl
+    path: _test\.go
diff --git a/github/activity_notifications.go b/github/activity_notifications.go
index 9b6fbe2b..009cc5e3 100644
--- a/github/activity_notifications.go
+++ b/github/activity_notifications.go
@@ -51,7 +51,7 @@ type NotificationListOptions struct {
 //
 // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/activity/#list-notifications-for-the-authenticated-user
 func (s *ActivityService) ListNotifications(ctx context.Context, opts *NotificationListOptions) ([]*Notification, *Response, error) {
-	u := fmt.Sprintf("notifications")
+	u := "notifications"
 	u, err := addOptions(u, opts)
 	if err != nil {
 		return nil, nil, err
diff --git a/github/github-accessors.go b/github/github-accessors.go
index bd94ba94..f869c9b3 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -1188,6 +1188,46 @@ func (a *AuthorizationUpdateRequest) GetNoteURL() string {
 	return *a.NoteURL
 }
 
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (a *Autolink) GetID() int64 {
+	if a == nil || a.ID == nil {
+		return 0
+	}
+	return *a.ID
+}
+
+// GetKeyPrefix returns the KeyPrefix field if it's non-nil, zero value otherwise.
+func (a *Autolink) GetKeyPrefix() string {
+	if a == nil || a.KeyPrefix == nil {
+		return ""
+	}
+	return *a.KeyPrefix
+}
+
+// GetURLTemplate returns the URLTemplate field if it's non-nil, zero value otherwise.
+func (a *Autolink) GetURLTemplate() string {
+	if a == nil || a.URLTemplate == nil {
+		return ""
+	}
+	return *a.URLTemplate
+}
+
+// GetKeyPrefix returns the KeyPrefix field if it's non-nil, zero value otherwise.
+func (a *AutolinkOptions) GetKeyPrefix() string {
+	if a == nil || a.KeyPrefix == nil {
+		return ""
+	}
+	return *a.KeyPrefix
+}
+
+// GetURLTemplate returns the URLTemplate field if it's non-nil, zero value otherwise.
+func (a *AutolinkOptions) GetURLTemplate() string {
+	if a == nil || a.URLTemplate == nil {
+		return ""
+	}
+	return *a.URLTemplate
+}
+
 // GetAppID returns the AppID field if it's non-nil, zero value otherwise.
 func (a *AutoTriggerCheck) GetAppID() int64 {
 	if a == nil || a.AppID == nil {
@@ -12564,6 +12604,14 @@ func (r *RepositoriesSearchResult) GetTotal() int {
 	return *r.Total
 }
 
+// GetAllowAutoMerge returns the AllowAutoMerge field if it's non-nil, zero value otherwise.
+func (r *Repository) GetAllowAutoMerge() bool {
+	if r == nil || r.AllowAutoMerge == nil {
+		return false
+	}
+	return *r.AllowAutoMerge
+}
+
 // GetAllowMergeCommit returns the AllowMergeCommit field if it's non-nil, zero value otherwise.
 func (r *Repository) GetAllowMergeCommit() bool {
 	if r == nil || r.AllowMergeCommit == nil {
diff --git a/github/repos.go b/github/repos.go
index eaadbb43..aab18d4d 100644
--- a/github/repos.go
+++ b/github/repos.go
@@ -60,6 +60,7 @@ type Repository struct {
 	AllowRebaseMerge    *bool           `json:"allow_rebase_merge,omitempty"`
 	AllowSquashMerge    *bool           `json:"allow_squash_merge,omitempty"`
 	AllowMergeCommit    *bool           `json:"allow_merge_commit,omitempty"`
+	AllowAutoMerge      *bool           `json:"allow_auto_merge,omitempty"`
 	DeleteBranchOnMerge *bool           `json:"delete_branch_on_merge,omitempty"`
 	Topics              []string        `json:"topics,omitempty"`
 	Archived            *bool           `json:"archived,omitempty"`
@@ -320,6 +321,7 @@ type createRepoRequest struct {
 	AllowSquashMerge    *bool   `json:"allow_squash_merge,omitempty"`
 	AllowMergeCommit    *bool   `json:"allow_merge_commit,omitempty"`
 	AllowRebaseMerge    *bool   `json:"allow_rebase_merge,omitempty"`
+	AllowAutoMerge      *bool   `json:"allow_auto_merge,omitempty"`
 	DeleteBranchOnMerge *bool   `json:"delete_branch_on_merge,omitempty"`
 }
 
@@ -362,6 +364,7 @@ func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repo
 		AllowSquashMerge:    repo.AllowSquashMerge,
 		AllowMergeCommit:    repo.AllowMergeCommit,
 		AllowRebaseMerge:    repo.AllowRebaseMerge,
+		AllowAutoMerge:      repo.AllowAutoMerge,
 		DeleteBranchOnMerge: repo.DeleteBranchOnMerge,
 	}
 
diff --git a/github/repos_autolinks.go b/github/repos_autolinks.go
new file mode 100644
index 00000000..b6404783
--- /dev/null
+++ b/github/repos_autolinks.go
@@ -0,0 +1,102 @@
+// Copyright 2021 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"
+)
+
+// AutolinkOptions specifies parameters for RepositoriesService.AddAutolink method.
+type AutolinkOptions struct {
+	KeyPrefix   *string `json:"key_prefix,omitempty"`
+	URLTemplate *string `json:"url_template,omitempty"`
+}
+
+// Autolink represents autolinks to external resources like JIRA issues and Zendesk tickets.
+type Autolink struct {
+	ID          *int64  `json:"id,omitempty"`
+	KeyPrefix   *string `json:"key_prefix,omitempty"`
+	URLTemplate *string `json:"url_template,omitempty"`
+}
+
+// ListAutolinks returns a list of autolinks configured for the given repository.
+// Information about autolinks are only available to repository administrators.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/repos#list-all-autolinks-of-a-repository
+func (s *RepositoriesService) ListAutolinks(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Autolink, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/autolinks", owner, repo)
+	u, err := addOptions(u, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var autolinks []*Autolink
+	resp, err := s.client.Do(ctx, req, &autolinks)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return autolinks, resp, nil
+}
+
+// AddAutolink creates an autolink reference for a repository.
+// Users with admin access to the repository can create an autolink.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/repos#create-an-autolink-reference-for-a-repository
+func (s *RepositoriesService) AddAutolink(ctx context.Context, owner, repo string, opts *AutolinkOptions) (*Autolink, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/autolinks", owner, repo)
+	req, err := s.client.NewRequest("POST", u, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	al := new(Autolink)
+	resp, err := s.client.Do(ctx, req, al)
+	if err != nil {
+		return nil, resp, err
+	}
+	return al, resp, nil
+}
+
+// GetAutolink returns a single autolink reference by ID that was configured for the given repository.
+// Information about autolinks are only available to repository administrators.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/repos#get-an-autolink-reference-of-a-repository
+func (s *RepositoriesService) GetAutolink(ctx context.Context, owner, repo string, id int64) (*Autolink, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/autolinks/%v", owner, repo, id)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var autolink *Autolink
+	resp, err := s.client.Do(ctx, req, &autolink)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return autolink, resp, nil
+}
+
+// DeleteAutolink deletes a single autolink reference by ID that was configured for the given repository.
+// Information about autolinks are only available to repository administrators.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/repos#delete-an-autolink-reference-from-a-repository
+func (s *RepositoriesService) DeleteAutolink(ctx context.Context, owner, repo string, id int64) (*Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/autolinks/%v", owner, repo, id)
+	req, err := s.client.NewRequest("DELETE", u, nil)
+	if err != nil {
+		return nil, err
+	}
+	return s.client.Do(ctx, req, nil)
+}
diff --git a/go.mod b/go.mod
index 1d8e20d3..91d67881 100644
--- a/go.mod
+++ b/go.mod
@@ -3,10 +3,10 @@ module github.com/google/go-github/v38
 require (
 	github.com/golang/protobuf v1.3.2 // indirect
 	github.com/google/go-cmp v0.5.6
-	github.com/google/go-querystring v1.0.0
+	github.com/google/go-querystring v1.1.0
 	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
 	golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
-	google.golang.org/appengine v1.1.0
+	google.golang.org/appengine v1.6.7
 )
 
 go 1.16
diff --git a/go.sum b/go.sum
index 7a4d739c..7e85c44d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,23 +1,30 @@
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
+github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
diff --git a/scrape/go.mod b/scrape/go.mod
index a083ff5b..ee95c062 100644
--- a/scrape/go.mod
+++ b/scrape/go.mod
@@ -3,8 +3,8 @@ module github.com/google/go-github/scrape
 go 1.13
 
 require (
-	github.com/PuerkitoBio/goquery v1.5.0
-	github.com/google/go-cmp v0.3.1
+	github.com/PuerkitoBio/goquery v1.7.1
+	github.com/google/go-cmp v0.5.6
 	github.com/google/go-github/v32 v32.1.0
 	github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
 	golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
diff --git a/scrape/go.sum b/scrape/go.sum
index d215f6ad..f20e765a 100644
--- a/scrape/go.sum
+++ b/scrape/go.sum
@@ -1,10 +1,10 @@
-github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
-github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
-github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
-github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4=
+github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY=
+github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
+github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
 github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
 github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@@ -14,8 +14,8 @@ github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -26,4 +26,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
diff --git a/update-urls/go.mod b/update-urls/go.mod
index 7c133f85..53666db8 100644
--- a/update-urls/go.mod
+++ b/update-urls/go.mod
@@ -2,4 +2,7 @@ module github.com/google/go-github/update-urls
 
 go 1.16
 
-require github.com/pmezard/go-difflib v1.0.0
+require (
+	github.com/google/go-cmp v0.5.6
+	github.com/pmezard/go-difflib v1.0.0
+)
diff --git a/update-urls/go.sum b/update-urls/go.sum
index 5d60ca74..cbfdfa22 100644
--- a/update-urls/go.sum
+++ b/update-urls/go.sum
@@ -1,2 +1,6 @@
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/update-urls/main.go b/update-urls/main.go
index a82ffbd9..248c7104 100644
--- a/update-urls/main.go
+++ b/update-urls/main.go
@@ -126,7 +126,9 @@ func main() {
 	}
 
 	if err := os.Chdir("./github"); err != nil {
-		log.Fatalf("Please run this from the go-github directory.")
+		if err := os.Chdir("../github"); err != nil {
+			log.Fatalf("Please run this from the go-github directory.")
+		}
 	}
 
 	pkgs, err := parser.ParseDir(fset, ".", sourceFilter, parser.ParseComments)
@@ -427,7 +429,7 @@ func (rafi *realAstFileIterator) Reset() {
 		var count int
 		for _, pkg := range rafi.pkgs {
 			for filename, f := range pkg.Files {
-				logf("Sending file #%v: %v to channel", count, filename)
+				// logf("Sending file #%v: %v to channel", count, filename)
 				rafi.ch <- &filenameAstFilePair{filename: filename, astFile: f}
 				count++
 			}
@@ -443,7 +445,7 @@ func (rafi *realAstFileIterator) Reset() {
 
 func (rafi *realAstFileIterator) Next() *filenameAstFilePair {
 	for pair := range rafi.ch {
-		logf("Next: returning file %v", pair.filename)
+		// logf("Next: returning file %v", pair.filename)
 		return pair
 	}
 	return nil
@@ -704,7 +706,7 @@ func processAST(filename string, f *ast.File, services servicesMap, endpoints en
 
 			receiverName := recv.Names[0].Name
 
-			logf("ast.FuncDecl: %#v", *decl)           // Doc, Recv, Name, Type, Body
+			logf("\n\nast.FuncDecl: %#v", *decl)       // Doc, Recv, Name, Type, Body
 			logf("ast.FuncDecl.Name: %#v", *decl.Name) // NamePos, Name, Obj(nil)
 			// logf("ast.FuncDecl.Recv: %#v", *decl.Recv)  // Opening, List, Closing
 			logf("ast.FuncDecl.Recv.List[0]: %#v", *recv) // Doc, Names, Type, Tag, Comment
@@ -1055,8 +1057,10 @@ func processCallExpr(expr *ast.CallExpr) (recv, funcName string, args []string)
 		case *ast.SelectorExpr: // X, Sel
 			logf("processCallExpr: X recv *ast.SelectorExpr: %#v", x.Sel)
 			recv = x.Sel.Name
+		case *ast.CallExpr: // Fun, LParen, Args, Ellipsis, RParen
+			logf("processCallExpr: X recv *ast.CallExpr: %#v", x)
 		default:
-			log.Fatalf("processCallExpr: unhandled X receiver type: %T", x)
+			log.Fatalf("processCallExpr: unhandled X receiver type: %T, funcName=%q", x, funcName)
 		}
 	default:
 		log.Fatalf("processCallExpr: unhandled Fun: %T", expr.Fun)
@@ -1191,9 +1195,7 @@ func parseEndpoint(s, method string) *Endpoint {
 	// 	eol = v
 	// }
 	path := strings.TrimSpace(s[len(method):eol])
-	if strings.HasPrefix(path, "{server}") { // Hack to remove {server}
-		path = strings.TrimPrefix(path, "{server}")
-	}
+	path = strings.TrimPrefix(path, "{server}")
 	path = paramLegacyRE.ReplaceAllString(path, "%v")
 	path = paramRE.ReplaceAllString(path, "%v")
 	// strip leading garbage


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 2b354674..f360911c 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -30,7 +30,7 @@ jobs:
 
     steps:
     - name: Cancel previous
-      uses: styfle/cancel-workflow-action@89f242ee29e10c53a841bfe71cc0ce7b2f065abc #0.9.0
+      uses: styfle/cancel-workflow-action@a40b8845c0683271d9f53dfcb887a7e181d3918b #0.9.1
       with:
         access_token: ${{ github.token }}
 
@@ -39,23 +39,24 @@ jobs:
         go-version: ${{ matrix.go-version }}
     - uses: actions/checkout@v2
 
+    # Get values for cache paths to be used in later steps 
+    - id: cache-paths
+      run: |
+        echo "::set-output name=go-cache::$(go env GOCACHE)"
+        echo "::set-output name=go-mod-cache::$(go env GOMODCACHE)"
+
     - name: Cache go modules
       uses: actions/cache@v2
       with:
-        path: ~/go/pkg/mod
+        path: |
+          ${{ steps.cache-paths.outputs.go-cache }}
+          ${{ steps.cache-paths.outputs.go-mod-cache }}
         key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
         restore-keys: ${{ runner.os }}-go-
 
-    - name: Run go fmt
-      if: runner.os != 'Windows'
-      run: diff -u <(echo -n) <(gofmt -d -s .)
-
     - name: Ensure go generate produces a zero diff
       shell: bash
-      run:  go generate -x ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
-
-    - name: Run go vet
-      run: go vet ./...
+      run: go generate -x ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
 
     - name: Run go test
       run: go test -v -race -coverprofile coverage.txt -covermode atomic ./...
@@ -71,4 +72,11 @@ jobs:
 
     - name: Upload coverage to Codecov
       if: ${{ matrix.update-coverage }}
-      uses: codecov/codecov-action@v1
+      uses: codecov/codecov-action@5a8bb4701eca7ba3673f21664b887f652c58d0a3 #v2.0.3
+
+    - name: Ensure go generate produces a zero diff for update-urls
+      shell: bash
+      run: cd update-urls && go generate -x ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code)
+
+    - name: Run go test for update-urls
+      run: cd update-urls && go test -v -race ./...
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 4264ce8b..e36aa7f8 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -1417,6 +1417,56 @@ func TestAuthorizationUpdateRequest_GetNoteURL(tt *testing.T) {
 	a.GetNoteURL()
 }
 
+func TestAutolink_GetID(tt *testing.T) {
+	var zeroValue int64
+	a := &Autolink{ID: &zeroValue}
+	a.GetID()
+	a = &Autolink{}
+	a.GetID()
+	a = nil
+	a.GetID()
+}
+
+func TestAutolink_GetKeyPrefix(tt *testing.T) {
+	var zeroValue string
+	a := &Autolink{KeyPrefix: &zeroValue}
+	a.GetKeyPrefix()
+	a = &Autolink{}
+	a.GetKeyPrefix()
+	a = nil
+	a.GetKeyPrefix()
+}
+
+func TestAutolink_GetURLTemplate(tt *testing.T) {
+	var zeroValue string
+	a := &Autolink{URLTemplate: &zeroValue}
+	a.GetURLTemplate()
+	a = &Autolink{}
+	a.GetURLTemplate()
+	a = nil
+	a.GetURLTemplate()
+}
+
+func TestAutolinkOptions_GetKeyPrefix(tt *testing.T) {
+	var zeroValue string
+	a := &AutolinkOptions{KeyPrefix: &zeroValue}
+	a.GetKeyPrefix()
+	a = &AutolinkOptions{}
+	a.GetKeyPrefix()
+	a = nil
+	a.GetKeyPrefix()
+}
+
+func TestAutolinkOptions_GetURLTemplate(tt *testing.T) {
+	var zeroValue string
+	a := &AutolinkOptions{URLTemplate: &zeroValue}
+	a.GetURLTemplate()
+	a = &AutolinkOptions{}
+	a.GetURLTemplate()
+	a = nil
+	a.GetURLTemplate()
+}
+
 func TestAutoTriggerCheck_GetAppID(tt *testing.T) {
 	var zeroValue int64
 	a := &AutoTriggerCheck{AppID: &zeroValue}
@@ -14641,6 +14691,16 @@ func TestRepositoriesSearchResult_GetTotal(tt *testing.T) {
 	r.GetTotal()
 }
 
+func TestRepository_GetAllowAutoMerge(tt *testing.T) {
+	var zeroValue bool
+	r := &Repository{AllowAutoMerge: &zeroValue}
+	r.GetAllowAutoMerge()
+	r = &Repository{}
+	r.GetAllowAutoMerge()
+	r = nil
+	r.GetAllowAutoMerge()
+}
+
 func TestRepository_GetAllowMergeCommit(tt *testing.T) {
 	var zeroValue bool
 	r := &Repository{AllowMergeCommit: &zeroValue}
diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go
index f56d7d4d..f3cab123 100644
--- a/github/github-stringify_test.go
+++ b/github/github-stringify_test.go
@@ -1422,6 +1422,7 @@ func TestRepository_String(t *testing.T) {
 		AllowRebaseMerge:    Bool(false),
 		AllowSquashMerge:    Bool(false),
 		AllowMergeCommit:    Bool(false),
+		AllowAutoMerge:      Bool(false),
 		DeleteBranchOnMerge: Bool(false),
 		Archived:            Bool(false),
 		Disabled:            Bool(false),
@@ -1475,7 +1476,7 @@ func TestRepository_String(t *testing.T) {
 		TeamsURL:            String(""),
 		Visibility:          String(""),
 	}
-	want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, OpenIssues:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Watchers:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, AllowRebaseMerge:false, AllowSquashMerge:false, AllowMergeCommit:false, DeleteBranchOnMerge:false, Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:"", Visibility:""}`
+	want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, OpenIssues:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Watchers:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, AllowRebaseMerge:false, AllowSquashMerge:false, AllowMergeCommit:false, AllowAutoMerge:false, DeleteBranchOnMerge:false, Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:"", Visibility:""}`
 	if got := v.String(); got != want {
 		t.Errorf("Repository.String = %v, want %v", got, want)
 	}
diff --git a/github/repos_autolinks_test.go b/github/repos_autolinks_test.go
new file mode 100644
index 00000000..b6121d2c
--- /dev/null
+++ b/github/repos_autolinks_test.go
@@ -0,0 +1,154 @@
+// Copyright 2021 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"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestRepositoriesService_ListAutolinks(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/autolinks", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testFormValues(t, r, values{"page": "2"})
+		fmt.Fprintf(w, `[{"id":1, "key_prefix": "TICKET-", "url_template": "https://example.com/TICKET?query=<num>"}, {"id":2, "key_prefix": "STORY-", "url_template": "https://example.com/STORY?query=<num>"}]`)
+	})
+
+	opt := &ListOptions{
+		Page: 2,
+	}
+	ctx := context.Background()
+	autolinks, _, err := client.Repositories.ListAutolinks(ctx, "o", "r", opt)
+	if err != nil {
+		t.Errorf("Repositories.ListAutolinks returned error: %v", err)
+	}
+
+	want := []*Autolink{
+		{ID: Int64(1), KeyPrefix: String("TICKET-"), URLTemplate: String("https://example.com/TICKET?query=<num>")},
+		{ID: Int64(2), KeyPrefix: String("STORY-"), URLTemplate: String("https://example.com/STORY?query=<num>")},
+	}
+
+	if !cmp.Equal(autolinks, want) {
+		t.Errorf("Repositories.ListAutolinks returned %+v, want %+v", autolinks, want)
+	}
+
+	const methodName = "ListAutolinks"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.ListAutolinks(ctx, "\n", "\n", opt)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.ListAutolinks(ctx, "o", "r", opt)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_AddAutolink(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	opt := &AutolinkOptions{KeyPrefix: String("TICKET-"), URLTemplate: String("https://example.com/TICKET?query=<num>")}
+	mux.HandleFunc("/repos/o/r/autolinks", func(w http.ResponseWriter, r *http.Request) {
+		v := new(AutolinkOptions)
+		json.NewDecoder(r.Body).Decode(v)
+		testMethod(t, r, "POST")
+		if !cmp.Equal(v, opt) {
+			t.Errorf("Request body = %+v, want %+v", v, opt)
+		}
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte(`{"key_prefix": "TICKET-","url_template": "https://example.com/TICKET?query=<num>"}`))
+	})
+	ctx := context.Background()
+	autolink, _, err := client.Repositories.AddAutolink(ctx, "o", "r", opt)
+	if err != nil {
+		t.Errorf("Repositories.AddAutolink returned error: %v", err)
+	}
+	want := &Autolink{
+		KeyPrefix:   String("TICKET-"),
+		URLTemplate: String("https://example.com/TICKET?query=<num>"),
+	}
+
+	if !cmp.Equal(autolink, want) {
+		t.Errorf("AddAutolink returned %+v, want %+v", autolink, want)
+	}
+
+	const methodName = "AddAutolink"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.AddAutolink(ctx, "\n", "\n", opt)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.AddAutolink(ctx, "o", "r", opt)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_GetAutolink(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/autolinks/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprintf(w, `{"id":1, "key_prefix": "TICKET-", "url_template": "https://example.com/TICKET?query=<num>"}`)
+	})
+
+	ctx := context.Background()
+	autolink, _, err := client.Repositories.GetAutolink(ctx, "o", "r", 1)
+	if err != nil {
+		t.Errorf("Repositories.GetAutolink returned error: %v", err)
+	}
+
+	want := &Autolink{ID: Int64(1), KeyPrefix: String("TICKET-"), URLTemplate: String("https://example.com/TICKET?query=<num>")}
+	if !cmp.Equal(autolink, want) {
+		t.Errorf("Repositories.GetAutolink returned %+v, want %+v", autolink, want)
+	}
+
+	const methodName = "GetAutolink"
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.GetAutolink(ctx, "o", "r", 2)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_DeleteAutolink(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/autolinks/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	ctx := context.Background()
+	_, err := client.Repositories.DeleteAutolink(ctx, "o", "r", 1)
+	if err != nil {
+		t.Errorf("Repositories.DeleteAutolink returned error: %v", err)
+	}
+
+	const methodName = "DeleteAutolink"
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Repositories.DeleteAutolink(ctx, "o", "r", 2)
+	})
+}
diff --git a/github/repos_environments_test.go b/github/repos_environments_test.go
index 535ae232..4165ad75 100644
--- a/github/repos_environments_test.go
+++ b/github/repos_environments_test.go
@@ -238,3 +238,91 @@ func TestRepositoriesService_DeleteEnvironment(t *testing.T) {
 		return client.Repositories.DeleteEnvironment(ctx, "o", "r", "e")
 	})
 }
+
+func TestRepoEnvironment_Marshal(t *testing.T) {
+	testJSONMarshal(t, &EnvResponse{}, "{}")
+
+	repoEnv := &EnvResponse{
+		TotalCount: Int(1),
+		Environments: []*Environment{
+			{
+				Owner:           String("me"),
+				Repo:            String("se"),
+				EnvironmentName: String("dev"),
+				WaitTimer:       Int(123),
+				Reviewers: []*EnvReviewers{
+					{
+						Type: String("main"),
+						ID:   Int64(1),
+					},
+					{
+						Type: String("rev"),
+						ID:   Int64(2),
+					},
+				},
+				DeploymentBranchPolicy: &BranchPolicy{
+					ProtectedBranches:    Bool(false),
+					CustomBranchPolicies: Bool(false),
+				},
+				ID:        Int64(2),
+				NodeID:    String("star"),
+				Name:      String("eg"),
+				URL:       String("https://hey.in"),
+				HTMLURL:   String("htmlurl"),
+				CreatedAt: &Timestamp{referenceTime},
+				UpdatedAt: &Timestamp{referenceTime},
+				ProtectionRules: []*ProtectionRule{
+					{
+						ID:        Int64(21),
+						NodeID:    String("mnb"),
+						Type:      String("ewq"),
+						WaitTimer: Int(9090),
+					},
+				},
+			},
+		},
+	}
+
+	want := `{
+		"total_count":1,
+		"environments":[
+		   {
+			  "owner":"me",
+			  "repo":"se",
+			  "environment_name":"dev",
+			  "wait_timer":123,
+			  "reviewers":[
+				 {
+					"type":"main",
+					"id":1
+				 },
+				 {
+					"type":"rev",
+					"id":2
+				 }
+			  ],
+			  "deployment_branch_policy":{
+				 "protected_branches":false,
+				 "custom_branch_policies":false
+			  },
+			  "id":2,
+			  "node_id":"star",
+			  "name":"eg",
+			  "url":"https://hey.in",
+			  "html_url":"htmlurl",
+			  "created_at":` + referenceTimeStr + `,
+			  "updated_at":` + referenceTimeStr + `,
+			  "protection_rules":[
+				 {
+					"id":21,
+					"node_id":"mnb",
+					"type":"ewq",
+					"wait_timer":9090
+				 }
+			  ]
+		   }
+		]
+	 }`
+
+	testJSONMarshal(t, repoEnv, want)
+}
diff --git a/github/repos_invitations_test.go b/github/repos_invitations_test.go
index e05ff3cd..74bda793 100644
--- a/github/repos_invitations_test.go
+++ b/github/repos_invitations_test.go
@@ -111,3 +111,55 @@ func TestRepositoriesService_UpdateInvitation(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestRepositoryInvitation_Marshal(t *testing.T) {
+	testJSONMarshal(t, &RepositoryInvitation{}, "{}")
+
+	r := &RepositoryInvitation{
+		ID: Int64(1),
+		Repo: &Repository{
+			ID:   Int64(1),
+			Name: String("n"),
+			URL:  String("u"),
+		},
+		Invitee: &User{
+			ID:   Int64(1),
+			Name: String("n"),
+			URL:  String("u"),
+		},
+		Inviter: &User{
+			ID:   Int64(1),
+			Name: String("n"),
+			URL:  String("u"),
+		},
+		Permissions: String("p"),
+		CreatedAt:   &Timestamp{referenceTime},
+		URL:         String("u"),
+		HTMLURL:     String("h"),
+	}
+
+	want := `{
+		"id":1,
+		"repository":{
+			"id":1,
+			"name":"n",
+			"url":"u"
+		},
+		"invitee":{
+			"id":1,
+			"name":"n",
+			"url":"u"
+		},
+		"inviter":{
+			"id":1,
+			"name":"n",
+			"url":"u"
+		},
+		"permissions":"p",
+		"created_at":` + referenceTimeStr + `,
+		"url":"u",
+		"html_url":"h"
+	}`
+
+	testJSONMarshal(t, r, want)
+}
diff --git a/github/repos_merging_test.go b/github/repos_merging_test.go
index 0d099552..05263100 100644
--- a/github/repos_merging_test.go
+++ b/github/repos_merging_test.go
@@ -62,3 +62,21 @@ func TestRepositoriesService_Merge(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestRepositoryMergeRequest_Marshal(t *testing.T) {
+	testJSONMarshal(t, &RepositoryMergeRequest{}, "{}")
+
+	u := &RepositoryMergeRequest{
+		Base:          String("base"),
+		Head:          String("head"),
+		CommitMessage: String("cm"),
+	}
+
+	want := `{
+		"base": "base",
+		"head": "head",
+		"commit_message": "cm"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_pages_test.go b/github/repos_pages_test.go
index 6a4d2ad5..85069f94 100644
--- a/github/repos_pages_test.go
+++ b/github/repos_pages_test.go
@@ -357,3 +357,133 @@ func TestRepositoriesService_RequestPageBuild(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestPagesSource_Marshal(t *testing.T) {
+	testJSONMarshal(t, &PagesSource{}, "{}")
+
+	u := &PagesSource{
+		Branch: String("branch"),
+		Path:   String("path"),
+	}
+
+	want := `{
+		"branch": "branch",
+		"path": "path"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestPagesError_Marshal(t *testing.T) {
+	testJSONMarshal(t, &PagesError{}, "{}")
+
+	u := &PagesError{
+		Message: String("message"),
+	}
+
+	want := `{
+		"message": "message"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestPagesUpdate_Marshal(t *testing.T) {
+	testJSONMarshal(t, &PagesUpdate{}, "{}")
+
+	u := &PagesUpdate{
+		CNAME:  String("cname"),
+		Source: String("src"),
+	}
+
+	want := `{
+		"cname": "cname",
+		"source": "src"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestPages_Marshal(t *testing.T) {
+	testJSONMarshal(t, &Pages{}, "{}")
+
+	u := &Pages{
+		URL:       String("url"),
+		Status:    String("status"),
+		CNAME:     String("cname"),
+		Custom404: Bool(false),
+		HTMLURL:   String("hurl"),
+		Source: &PagesSource{
+			Branch: String("branch"),
+			Path:   String("path"),
+		},
+	}
+
+	want := `{
+		"url": "url",
+		"status": "status",
+		"cname": "cname",
+		"custom_404": false,
+		"html_url": "hurl",
+		"source": {
+			"branch": "branch",
+			"path": "path"
+		}
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestPagesBuild_Marshal(t *testing.T) {
+	testJSONMarshal(t, &PagesBuild{}, "{}")
+
+	u := &PagesBuild{
+		URL:    String("url"),
+		Status: String("status"),
+		Error: &PagesError{
+			Message: String("message"),
+		},
+		Pusher:    &User{ID: Int64(1)},
+		Commit:    String("commit"),
+		Duration:  Int(1),
+		CreatedAt: &Timestamp{referenceTime},
+		UpdatedAt: &Timestamp{referenceTime},
+	}
+
+	want := `{
+		"url": "url",
+		"status": "status",
+		"error": {
+			"message": "message"
+		},
+		"pusher": {
+			"id": 1
+		},
+		"commit": "commit",
+		"duration": 1,
+		"created_at": ` + referenceTimeStr + `,
+		"updated_at": ` + referenceTimeStr + `
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestCreatePagesRequest_Marshal(t *testing.T) {
+	testJSONMarshal(t, &createPagesRequest{}, "{}")
+
+	u := &createPagesRequest{
+		Source: &PagesSource{
+			Branch: String("branch"),
+			Path:   String("path"),
+		},
+	}
+
+	want := `{
+		"source": {
+			"branch": "branch",
+			"path": "path"
+		}
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_prereceive_hooks_test.go b/github/repos_prereceive_hooks_test.go
index 90178bd5..303e0e12 100644
--- a/github/repos_prereceive_hooks_test.go
+++ b/github/repos_prereceive_hooks_test.go
@@ -194,3 +194,23 @@ func TestRepositoriesService_DeletePreReceiveHook_invalidOwner(t *testing.T) {
 	_, err := client.Repositories.DeletePreReceiveHook(ctx, "%", "%", 1)
 	testURLParseError(t, err)
 }
+
+func TestPreReceiveHook_Marshal(t *testing.T) {
+	testJSONMarshal(t, &PreReceiveHook{}, "{}")
+
+	u := &PreReceiveHook{
+		ID:          Int64(1),
+		Name:        String("name"),
+		Enforcement: String("e"),
+		ConfigURL:   String("curl"),
+	}
+
+	want := `{
+		"id": 1,
+		"name": "name",
+		"enforcement": "e",
+		"configuration_url": "curl"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_releases_test.go b/github/repos_releases_test.go
index 9f1fbe8d..69041c0c 100644
--- a/github/repos_releases_test.go
+++ b/github/repos_releases_test.go
@@ -653,3 +653,125 @@ func TestRepositoriesService_UploadReleaseAsset(t *testing.T) {
 		})
 	}
 }
+
+func TestRepositoryReleaseRequest_Marshal(t *testing.T) {
+	testJSONMarshal(t, &repositoryReleaseRequest{}, "{}")
+
+	u := &repositoryReleaseRequest{
+		TagName:                String("tn"),
+		TargetCommitish:        String("tc"),
+		Name:                   String("name"),
+		Body:                   String("body"),
+		Draft:                  Bool(false),
+		Prerelease:             Bool(false),
+		DiscussionCategoryName: String("dcn"),
+	}
+
+	want := `{
+		"tag_name": "tn",
+		"target_commitish": "tc",
+		"name": "name",
+		"body": "body",
+		"draft": false,
+		"prerelease": false,
+		"discussion_category_name": "dcn"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestReleaseAsset_Marshal(t *testing.T) {
+	testJSONMarshal(t, &ReleaseAsset{}, "{}")
+
+	u := &ReleaseAsset{
+		ID:                 Int64(1),
+		URL:                String("url"),
+		Name:               String("name"),
+		Label:              String("label"),
+		State:              String("state"),
+		ContentType:        String("ct"),
+		Size:               Int(1),
+		DownloadCount:      Int(1),
+		CreatedAt:          &Timestamp{referenceTime},
+		UpdatedAt:          &Timestamp{referenceTime},
+		BrowserDownloadURL: String("bdu"),
+		Uploader:           &User{ID: Int64(1)},
+		NodeID:             String("nid"),
+	}
+
+	want := `{
+		"id": 1,
+		"url": "url",
+		"name": "name",
+		"label": "label",
+		"state": "state",
+		"content_type": "ct",
+		"size": 1,
+		"download_count": 1,
+		"created_at": ` + referenceTimeStr + `,
+		"updated_at": ` + referenceTimeStr + `,
+		"browser_download_url": "bdu",
+		"uploader": {
+			"id": 1
+		},
+		"node_id": "nid"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestRepositoryRelease_Marshal(t *testing.T) {
+	testJSONMarshal(t, &RepositoryRelease{}, "{}")
+
+	u := &RepositoryRelease{
+		TagName:                String("tn"),
+		TargetCommitish:        String("tc"),
+		Name:                   String("name"),
+		Body:                   String("body"),
+		Draft:                  Bool(false),
+		Prerelease:             Bool(false),
+		DiscussionCategoryName: String("dcn"),
+		ID:                     Int64(1),
+		CreatedAt:              &Timestamp{referenceTime},
+		PublishedAt:            &Timestamp{referenceTime},
+		URL:                    String("url"),
+		HTMLURL:                String("hurl"),
+		AssetsURL:              String("aurl"),
+		Assets:                 []*ReleaseAsset{{ID: Int64(1)}},
+		UploadURL:              String("uurl"),
+		ZipballURL:             String("zurl"),
+		TarballURL:             String("turl"),
+		Author:                 &User{ID: Int64(1)},
+		NodeID:                 String("nid"),
+	}
+
+	want := `{
+		"tag_name": "tn",
+		"target_commitish": "tc",
+		"name": "name",
+		"body": "body",
+		"draft": false,
+		"prerelease": false,
+		"discussion_category_name": "dcn",
+		"id": 1,
+		"created_at": ` + referenceTimeStr + `,
+		"published_at": ` + referenceTimeStr + `,
+		"url": "url",
+		"html_url": "hurl",
+		"assets_url": "aurl",
+		"assets": [
+			{
+				"id": 1
+			}
+		],
+		"upload_url": "uurl",
+		"zipball_url": "zurl",
+		"tarball_url": "turl",
+		"author": {
+			"id": 1
+		},
+		"node_id": "nid"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_stats_test.go b/github/repos_stats_test.go
index efb36674..1babaca6 100644
--- a/github/repos_stats_test.go
+++ b/github/repos_stats_test.go
@@ -327,3 +327,93 @@ func TestRepositoriesService_AcceptedError(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestRepositoryParticipation_Marshal(t *testing.T) {
+	testJSONMarshal(t, &RepositoryParticipation{}, "{}")
+
+	u := &RepositoryParticipation{
+		All:   []int{1},
+		Owner: []int{1},
+	}
+
+	want := `{
+		"all": [1],
+		"owner": [1]
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestWeeklyCommitActivity_Marshal(t *testing.T) {
+	testJSONMarshal(t, &WeeklyCommitActivity{}, "{}")
+
+	u := &WeeklyCommitActivity{
+		Days:  []int{1},
+		Total: Int(1),
+		Week:  &Timestamp{referenceTime},
+	}
+
+	want := `{
+		"days": [
+			1
+		],
+		"total": 1,
+		"week": ` + referenceTimeStr + `
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestWeeklyStats_Marshal(t *testing.T) {
+	testJSONMarshal(t, &WeeklyStats{}, "{}")
+
+	u := &WeeklyStats{
+		Week:      &Timestamp{referenceTime},
+		Additions: Int(1),
+		Deletions: Int(1),
+		Commits:   Int(1),
+	}
+
+	want := `{
+		"w": ` + referenceTimeStr + `,
+		"a": 1,
+		"d": 1,
+		"c": 1
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestContributorStats_Marshal(t *testing.T) {
+	testJSONMarshal(t, &ContributorStats{}, "{}")
+
+	u := &ContributorStats{
+		Author: &Contributor{ID: Int64(1)},
+		Total:  Int(1),
+		Weeks: []*WeeklyStats{
+			{
+				Week:      &Timestamp{referenceTime},
+				Additions: Int(1),
+				Deletions: Int(1),
+				Commits:   Int(1),
+			},
+		},
+	}
+
+	want := `{
+		"author": {
+			"id": 1
+		},
+		"total": 1,
+		"weeks": [
+			{
+				"w": ` + referenceTimeStr + `,
+				"a": 1,
+				"d": 1,
+				"c": 1
+			}
+		]
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_statuses_test.go b/github/repos_statuses_test.go
index 3fa2ca7c..62fe3336 100644
--- a/github/repos_statuses_test.go
+++ b/github/repos_statuses_test.go
@@ -149,3 +149,95 @@ func TestRepositoriesService_GetCombinedStatus(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestRepoStatus_Marshal(t *testing.T) {
+	testJSONMarshal(t, &RepoStatus{}, "{}")
+
+	u := &RepoStatus{
+		ID:          Int64(1),
+		NodeID:      String("nid"),
+		URL:         String("url"),
+		State:       String("state"),
+		TargetURL:   String("turl"),
+		Description: String("desc"),
+		Context:     String("ctx"),
+		AvatarURL:   String("aurl"),
+		Creator:     &User{ID: Int64(1)},
+		CreatedAt:   &referenceTime,
+		UpdatedAt:   &referenceTime,
+	}
+
+	want := `{
+		"id": 1,
+		"node_id": "nid",
+		"url": "url",
+		"state": "state",
+		"target_url": "turl",
+		"description": "desc",
+		"context": "ctx",
+		"avatar_url": "aurl",
+		"creator": {
+			"id": 1
+		},
+		"created_at": ` + referenceTimeStr + `,
+		"updated_at": ` + referenceTimeStr + `
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestCombinedStatus_Marshal(t *testing.T) {
+	testJSONMarshal(t, &CombinedStatus{}, "{}")
+
+	u := &CombinedStatus{
+		State:      String("state"),
+		Name:       String("name"),
+		SHA:        String("sha"),
+		TotalCount: Int(1),
+		Statuses: []*RepoStatus{
+			{
+				ID:          Int64(1),
+				NodeID:      String("nid"),
+				URL:         String("url"),
+				State:       String("state"),
+				TargetURL:   String("turl"),
+				Description: String("desc"),
+				Context:     String("ctx"),
+				AvatarURL:   String("aurl"),
+				Creator:     &User{ID: Int64(1)},
+				CreatedAt:   &referenceTime,
+				UpdatedAt:   &referenceTime,
+			},
+		},
+		CommitURL:     String("curl"),
+		RepositoryURL: String("rurl"),
+	}
+
+	want := `{
+		"state": "state",
+		"name": "name",
+		"sha": "sha",
+		"total_count": 1,
+		"statuses": [
+			{
+				"id": 1,
+				"node_id": "nid",
+				"url": "url",
+				"state": "state",
+				"target_url": "turl",
+				"description": "desc",
+				"context": "ctx",
+				"avatar_url": "aurl",
+				"creator": {
+					"id": 1
+				},
+				"created_at": ` + referenceTimeStr + `,
+				"updated_at": ` + referenceTimeStr + `
+			}
+		],
+		"commit_url": "curl",
+		"repository_url": "rurl"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/update-urls/main_test.go b/update-urls/main_test.go
index 9db289a1..e9c5ef3d 100644
--- a/update-urls/main_test.go
+++ b/update-urls/main_test.go
@@ -433,6 +433,10 @@ func TestSortAndMergeFileEdits(t *testing.T) {
 		},
 	}
 
+	fileEditEqual := cmp.Comparer(func(a, b *FileEdit) bool {
+		return a.fromText == b.fromText && a.pos == b.pos && a.toText == b.toText
+	})
+
 	for i, tt := range tests {
 		t.Run(fmt.Sprintf("test #%v: %v", i, tt.name), func(t *testing.T) {
 			got := sortAndMergeFileEdits(tt.fileEdits)
@@ -445,7 +449,7 @@ func TestSortAndMergeFileEdits(t *testing.T) {
 				if i < len(tt.want) {
 					wantFileEdit = tt.want[i]
 				}
-				if !cmp.Equal(got[i], wantFileEdit) {
+				if !cmp.Equal(got[i], wantFileEdit, fileEditEqual) {
 					t.Errorf("got[%v] =\n%#v\nwant[%v]:\n%#v", i, got[i], i, wantFileEdit)
 				}
 			}
@@ -538,6 +542,13 @@ func TestGitURL(t *testing.T) {
 	}
 }
 
+var endpointEqual = cmp.Comparer(func(a, b *Endpoint) bool {
+	if a.httpMethod != b.httpMethod {
+		return false
+	}
+	return cmp.Equal(a.urlFormats, b.urlFormats)
+})
+
 func testWebPageHelper(t *testing.T, got, want map[string][]*Endpoint) {
 	t.Helper()
 
@@ -551,7 +562,7 @@ func testWebPageHelper(t *testing.T, got, want map[string][]*Endpoint) {
 			if ok && i < len(w) {
 				wantEndpoint = w[i]
 			}
-			if !cmp.Equal(got[k][i], wantEndpoint) {
+			if !cmp.Equal(got[k][i], wantEndpoint, endpointEqual) {
 				t.Errorf("got[%q][%v] =\n%#v\nwant[%q][%v]:\n%#v", k, i, got[k][i], k, i, wantEndpoint)
 			}
 		}
@@ -590,7 +601,7 @@ func TestParseEndpoint(t *testing.T) {
 		t.Run(fmt.Sprintf("test #%v: %v", i, tt.name), func(t *testing.T) {
 			got := parseEndpoint(tt.s, tt.method)
 
-			if !cmp.Equal(got, tt.want) {
+			if !cmp.Equal(got, tt.want, endpointEqual) {
 				t.Errorf("parseEndpoint = %#v, want %#v", got, tt.want)
 			}
 		})