Repository: google/go-github
Pull request #2711 (2023-03-19 10:23:57)

Set authenticated user's email visibility via API
Fixes: https://github.com/google/go-github/issues/2688

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

```diff
@@           Coverage Diff           @@
##           master    #2711   +/-   ##
=======================================
  Coverage   98.05%   98.05%           
=======================================
  Files         131      131           
  Lines       11459    11471   +12     
=======================================
+ Hits        11236    11248   +12     
  Misses        152      152           
  Partials       71       71           
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/2711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) | Coverage Δ | |
|---|---|---|
| [github/users\_emails.go](https://codecov.io/gh/google/go-github/pull/2711?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL3VzZXJzX2VtYWlscy5nbw==) | `93.61% <100.00%> (+2.18%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)

gmlewis: @Sairaviteja27 - please remove the "Draft" status when you want a PR to be reviewed.
Sairaviteja27: Hi @gmlewis , marked it for review, thanks.

Patch:
diff --git a/github/actions_workflow_runs.go b/github/actions_workflow_runs.go
index 0cda90d4..b37a9f31 100644
--- a/github/actions_workflow_runs.go
+++ b/github/actions_workflow_runs.go
@@ -204,6 +204,26 @@ func (s *ActionsService) GetWorkflowRunAttempt(ctx context.Context, owner, repo
 	return run, resp, nil
 }
 
+// GetWorkflowRunAttemptLogs gets a redirect URL to download a plain text file of logs for a workflow run for attempt number.
+//
+// GitHub API docs: https://docs.github.com/en/rest/actions/workflow-runs#download-workflow-run-attempt-logs
+func (s *ActionsService) GetWorkflowRunAttemptLogs(ctx context.Context, owner, repo string, runID int64, attemptNumber int, followRedirects bool) (*url.URL, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/attempts/%v/logs", owner, repo, runID, attemptNumber)
+
+	resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, followRedirects)
+	if err != nil {
+		return nil, nil, err
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusFound {
+		return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status)
+	}
+
+	parsedURL, err := url.Parse(resp.Header.Get("Location"))
+	return parsedURL, newResponse(resp), err
+}
+
 // RerunWorkflowByID re-runs a workflow by ID.
 //
 // GitHub API docs: https://docs.github.com/en/rest/actions/workflow-runs#re-run-a-workflow
diff --git a/github/billing.go b/github/billing.go
index 2900a016..7a76bf86 100644
--- a/github/billing.go
+++ b/github/billing.go
@@ -120,9 +120,14 @@ func (s *BillingService) GetStorageBillingOrg(ctx context.Context, org string) (
 
 // GetAdvancedSecurityActiveCommittersOrg returns the GitHub Advanced Security active committers for an organization per repository.
 //
-// GitHub API docs: https://docs.github.com/en/rest/billing#get-github-advanced-security-active-committers-for-an-organization
-func (s *BillingService) GetAdvancedSecurityActiveCommittersOrg(ctx context.Context, org string) (*ActiveCommitters, *Response, error) {
+// GitHub API docs: https://docs.github.com/en/enterprise-cloud@latest/rest/billing?apiVersion=2022-11-28#get-github-advanced-security-active-committers-for-an-organization
+func (s *BillingService) GetAdvancedSecurityActiveCommittersOrg(ctx context.Context, org string, opts *ListOptions) (*ActiveCommitters, *Response, error) {
 	u := fmt.Sprintf("orgs/%v/settings/billing/advanced-security", org)
+	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
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 9c435048..b814fd8d 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -4022,6 +4022,14 @@ func (c *CreateRunnerGroupRequest) GetVisibility() string {
 	return *c.Visibility
 }
 
+// GetCanAdminsBypass returns the CanAdminsBypass field if it's non-nil, zero value otherwise.
+func (c *CreateUpdateEnvironment) GetCanAdminsBypass() bool {
+	if c == nil || c.CanAdminsBypass == nil {
+		return false
+	}
+	return *c.CanAdminsBypass
+}
+
 // GetDeploymentBranchPolicy returns the DeploymentBranchPolicy field.
 func (c *CreateUpdateEnvironment) GetDeploymentBranchPolicy() *BranchPolicy {
 	if c == nil {
@@ -11774,6 +11782,14 @@ func (p *PageBuildEvent) GetSender() *User {
 	return p.Sender
 }
 
+// GetBuildType returns the BuildType field if it's non-nil, zero value otherwise.
+func (p *Pages) GetBuildType() string {
+	if p == nil || p.BuildType == nil {
+		return ""
+	}
+	return *p.BuildType
+}
+
 // GetCNAME returns the CNAME field if it's non-nil, zero value otherwise.
 func (p *Pages) GetCNAME() string {
 	if p == nil || p.CNAME == nil {
@@ -11966,6 +11982,14 @@ func (p *PageStats) GetTotalPages() int {
 	return *p.TotalPages
 }
 
+// GetBuildType returns the BuildType field if it's non-nil, zero value otherwise.
+func (p *PagesUpdate) GetBuildType() string {
+	if p == nil || p.BuildType == nil {
+		return ""
+	}
+	return *p.BuildType
+}
+
 // GetCNAME returns the CNAME field if it's non-nil, zero value otherwise.
 func (p *PagesUpdate) GetCNAME() string {
 	if p == nil || p.CNAME == nil {
diff --git a/github/repos_environments.go b/github/repos_environments.go
index 7f01be62..2399a42c 100644
--- a/github/repos_environments.go
+++ b/github/repos_environments.go
@@ -148,11 +148,15 @@ func (s *RepositoriesService) GetEnvironment(ctx context.Context, owner, repo, n
 
 // MarshalJSON implements the json.Marshaler interface.
 // As the only way to clear a WaitTimer is to set it to 0, a missing WaitTimer object should default to 0, not null.
+// As the default value for CanAdminsBypass is true, a nil value here marshals to true.
 func (c *CreateUpdateEnvironment) MarshalJSON() ([]byte, error) {
 	type Alias CreateUpdateEnvironment
 	if c.WaitTimer == nil {
 		c.WaitTimer = Int(0)
 	}
+	if c.CanAdminsBypass == nil {
+		c.CanAdminsBypass = Bool(true)
+	}
 	return json.Marshal(&struct {
 		*Alias
 	}{
@@ -167,6 +171,7 @@ func (c *CreateUpdateEnvironment) MarshalJSON() ([]byte, error) {
 type CreateUpdateEnvironment struct {
 	WaitTimer              *int            `json:"wait_timer"`
 	Reviewers              []*EnvReviewers `json:"reviewers"`
+	CanAdminsBypass        *bool           `json:"can_admins_bypass"`
 	DeploymentBranchPolicy *BranchPolicy   `json:"deployment_branch_policy"`
 }
 
diff --git a/github/repos_pages.go b/github/repos_pages.go
index 737cec0f..845de8a0 100644
--- a/github/repos_pages.go
+++ b/github/repos_pages.go
@@ -17,6 +17,7 @@ type Pages struct {
 	CNAME            *string                `json:"cname,omitempty"`
 	Custom404        *bool                  `json:"custom_404,omitempty"`
 	HTMLURL          *string                `json:"html_url,omitempty"`
+	BuildType        *string                `json:"build_type,omitempty"`
 	Source           *PagesSource           `json:"source,omitempty"`
 	Public           *bool                  `json:"public,omitempty"`
 	HTTPSCertificate *PagesHTTPSCertificate `json:"https_certificate,omitempty"`
@@ -58,7 +59,8 @@ type PagesHTTPSCertificate struct {
 // createPagesRequest is a subset of Pages and is used internally
 // by EnablePages to pass only the known fields for the endpoint.
 type createPagesRequest struct {
-	Source *PagesSource `json:"source,omitempty"`
+	BuildType *string      `json:"build_type,omitempty"`
+	Source    *PagesSource `json:"source,omitempty"`
 }
 
 // EnablePages enables GitHub Pages for the named repo.
@@ -68,7 +70,8 @@ func (s *RepositoriesService) EnablePages(ctx context.Context, owner, repo strin
 	u := fmt.Sprintf("repos/%v/%v/pages", owner, repo)
 
 	pagesReq := &createPagesRequest{
-		Source: pages.Source,
+		BuildType: pages.BuildType,
+		Source:    pages.Source,
 	}
 
 	req, err := s.client.NewRequest("POST", u, pagesReq)
@@ -92,6 +95,10 @@ type PagesUpdate struct {
 	// CNAME represents a custom domain for the repository.
 	// Leaving CNAME empty will remove the custom domain.
 	CNAME *string `json:"cname"`
+	// BuildType is optional and can either be "legacy" or "workflow".
+	// "workflow" - You are using a github workflow to build your pages.
+	// "legacy"   - You are deploying from a branch.
+	BuildType *string `json:"build_type,omitempty"`
 	// Source must include the branch name, and may optionally specify the subdirectory "/docs".
 	// Possible values for Source.Branch are usually "gh-pages", "main", and "master",
 	// or any other existing branch name.
diff --git a/github/users_emails.go b/github/users_emails.go
index be7e0f81..67bd210e 100644
--- a/github/users_emails.go
+++ b/github/users_emails.go
@@ -70,3 +70,28 @@ func (s *UsersService) DeleteEmails(ctx context.Context, emails []string) (*Resp
 
 	return s.client.Do(ctx, req, nil)
 }
+
+// SetEmailVisibility sets the visibility for the primary email address of the authenticated user.
+// `visibility` can be "private" or "public".
+//
+// GitHub API docs: https://docs.github.com/en/rest/users/emails#set-primary-email-visibility-for-the-authenticated-user
+func (s *UsersService) SetEmailVisibility(ctx context.Context, visibility string) ([]*UserEmail, *Response, error) {
+	u := "user/email/visibility"
+
+	updateVisiblilityReq := &UserEmail{
+		Visibility: &visibility,
+	}
+
+	req, err := s.client.NewRequest("PATCH", u, updateVisiblilityReq)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var e []*UserEmail
+	resp, err := s.client.Do(ctx, req, &e)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return e, resp, nil
+}


Test patch:
diff --git a/github/actions_workflow_runs_test.go b/github/actions_workflow_runs_test.go
index 641c4d73..74b5bdd0 100644
--- a/github/actions_workflow_runs_test.go
+++ b/github/actions_workflow_runs_test.go
@@ -188,6 +188,89 @@ func TestActionsService_GetWorkflowRunAttempt(t *testing.T) {
 	})
 }
 
+func TestActionsService_GetWorkflowRunAttemptLogs(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/runs/399444496/attempts/2/logs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
+	})
+
+	ctx := context.Background()
+	url, resp, err := client.Actions.GetWorkflowRunAttemptLogs(ctx, "o", "r", 399444496, 2, true)
+	if err != nil {
+		t.Errorf("Actions.GetWorkflowRunAttemptLogs returned error: %v", err)
+	}
+	if resp.StatusCode != http.StatusFound {
+		t.Errorf("Actions.GetWorkflowRunAttemptLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound)
+	}
+	want := "http://github.com/a"
+	if url.String() != want {
+		t.Errorf("Actions.GetWorkflowRunAttemptLogs returned %+v, want %+v", url.String(), want)
+	}
+
+	const methodName = "GetWorkflowRunAttemptLogs"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.GetWorkflowRunAttemptLogs(ctx, "\n", "\n", 399444496, 2, true)
+		return err
+	})
+}
+
+func TestActionsService_GetWorkflowRunAttemptLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/runs/399444496/attempts/2/logs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently)
+	})
+
+	ctx := context.Background()
+	_, resp, _ := client.Actions.GetWorkflowRunAttemptLogs(ctx, "o", "r", 399444496, 2, false)
+	if resp.StatusCode != http.StatusMovedPermanently {
+		t.Errorf("Actions.GetWorkflowRunAttemptLogs returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently)
+	}
+}
+
+func TestActionsService_GetWorkflowRunAttemptLogs_StatusMovedPermanently_followRedirects(t *testing.T) {
+	client, mux, serverURL, teardown := setup()
+	defer teardown()
+
+	// Mock a redirect link, which leads to an archive link
+	mux.HandleFunc("/repos/o/r/actions/runs/399444496/attempts/2/logs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
+		http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
+	})
+
+	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
+	})
+
+	ctx := context.Background()
+	url, resp, err := client.Actions.GetWorkflowRunAttemptLogs(ctx, "o", "r", 399444496, 2, true)
+	if err != nil {
+		t.Errorf("Actions.GetWorkflowRunAttemptLogs returned error: %v", err)
+	}
+
+	if resp.StatusCode != http.StatusFound {
+		t.Errorf("Actions.GetWorkflowRunAttemptLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound)
+	}
+
+	want := "http://github.com/a"
+	if url.String() != want {
+		t.Errorf("Actions.GetWorkflowRunAttemptLogs returned %+v, want %+v", url.String(), want)
+	}
+
+	const methodName = "GetWorkflowRunAttemptLogs"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.GetWorkflowRunAttemptLogs(ctx, "\n", "\n", 399444496, 2, true)
+		return err
+	})
+}
+
 func TestActionsService_RerunWorkflowRunByID(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/billing_test.go b/github/billing_test.go
index 52b338b8..d7adcb09 100644
--- a/github/billing_test.go
+++ b/github/billing_test.go
@@ -452,7 +452,8 @@ func TestBillingService_GetAdvancedSecurityActiveCommittersOrg(t *testing.T) {
 	})
 
 	ctx := context.Background()
-	hook, _, err := client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "o")
+	opts := &ListOptions{Page: 2, PerPage: 50}
+	hook, _, err := client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "o", opts)
 	if err != nil {
 		t.Errorf("Billing.GetAdvancedSecurityActiveCommittersOrg	 returned error: %v", err)
 	}
@@ -478,12 +479,12 @@ func TestBillingService_GetAdvancedSecurityActiveCommittersOrg(t *testing.T) {
 
 	const methodName = "GetAdvancedSecurityActiveCommittersOrg"
 	testBadOptions(t, methodName, func() (err error) {
-		_, _, err = client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "\n")
+		_, _, err = client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "\n", nil)
 		return err
 	})
 
 	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
-		got, resp, err := client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "o")
+		got, resp, err := client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "o", nil)
 		if got != nil {
 			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
 		}
@@ -496,6 +497,6 @@ func TestBillingService_GetAdvancedSecurityActiveCommittersOrg_invalidOrg(t *tes
 	defer teardown()
 
 	ctx := context.Background()
-	_, _, err := client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "%")
+	_, _, err := client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "%", nil)
 	testURLParseError(t, err)
 }
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 5091cc46..efdc3f64 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -4725,6 +4725,16 @@ func TestCreateRunnerGroupRequest_GetVisibility(tt *testing.T) {
 	c.GetVisibility()
 }
 
+func TestCreateUpdateEnvironment_GetCanAdminsBypass(tt *testing.T) {
+	var zeroValue bool
+	c := &CreateUpdateEnvironment{CanAdminsBypass: &zeroValue}
+	c.GetCanAdminsBypass()
+	c = &CreateUpdateEnvironment{}
+	c.GetCanAdminsBypass()
+	c = nil
+	c.GetCanAdminsBypass()
+}
+
 func TestCreateUpdateEnvironment_GetDeploymentBranchPolicy(tt *testing.T) {
 	c := &CreateUpdateEnvironment{}
 	c.GetDeploymentBranchPolicy()
@@ -13806,6 +13816,16 @@ func TestPageBuildEvent_GetSender(tt *testing.T) {
 	p.GetSender()
 }
 
+func TestPages_GetBuildType(tt *testing.T) {
+	var zeroValue string
+	p := &Pages{BuildType: &zeroValue}
+	p.GetBuildType()
+	p = &Pages{}
+	p.GetBuildType()
+	p = nil
+	p.GetBuildType()
+}
+
 func TestPages_GetCNAME(tt *testing.T) {
 	var zeroValue string
 	p := &Pages{CNAME: &zeroValue}
@@ -14034,6 +14054,16 @@ func TestPageStats_GetTotalPages(tt *testing.T) {
 	p.GetTotalPages()
 }
 
+func TestPagesUpdate_GetBuildType(tt *testing.T) {
+	var zeroValue string
+	p := &PagesUpdate{BuildType: &zeroValue}
+	p.GetBuildType()
+	p = &PagesUpdate{}
+	p.GetBuildType()
+	p = nil
+	p.GetBuildType()
+}
+
 func TestPagesUpdate_GetCNAME(tt *testing.T) {
 	var zeroValue string
 	p := &PagesUpdate{CNAME: &zeroValue}
diff --git a/github/repos_environments_test.go b/github/repos_environments_test.go
index a688b221..0f93d290 100644
--- a/github/repos_environments_test.go
+++ b/github/repos_environments_test.go
@@ -93,7 +93,7 @@ func TestCreateUpdateEnvironment_MarshalJSON(t *testing.T) {
 		t.Errorf("MarshalJSON: %v", err)
 	}
 
-	want := `{"wait_timer":0,"reviewers":null,"deployment_branch_policy":null}`
+	want := `{"wait_timer":0,"reviewers":null,"can_admins_bypass":true,"deployment_branch_policy":null}`
 	if string(got) != want {
 		t.Errorf("MarshalJSON = %s, want %v", got, want)
 	}
@@ -187,7 +187,7 @@ func TestRepositoriesService_CreateEnvironment(t *testing.T) {
 		json.NewDecoder(r.Body).Decode(v)
 
 		testMethod(t, r, "PUT")
-		want := &CreateUpdateEnvironment{WaitTimer: Int(30)}
+		want := &CreateUpdateEnvironment{WaitTimer: Int(30), CanAdminsBypass: Bool(true)}
 		if !cmp.Equal(v, want) {
 			t.Errorf("Request body = %+v, want %+v", v, want)
 		}
diff --git a/github/repos_pages_test.go b/github/repos_pages_test.go
index 75d6ccb2..6245aac2 100644
--- a/github/repos_pages_test.go
+++ b/github/repos_pages_test.go
@@ -17,11 +17,12 @@ import (
 	"github.com/google/go-cmp/cmp"
 )
 
-func TestRepositoriesService_EnablePages(t *testing.T) {
+func TestRepositoriesService_EnablePagesLegacy(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
 
 	input := &Pages{
+		BuildType: String("legacy"),
 		Source: &PagesSource{
 			Branch: String("master"),
 			Path:   String("/"),
@@ -35,12 +36,12 @@ func TestRepositoriesService_EnablePages(t *testing.T) {
 
 		testMethod(t, r, "POST")
 		testHeader(t, r, "Accept", mediaTypeEnablePagesAPIPreview)
-		want := &createPagesRequest{Source: &PagesSource{Branch: String("master"), Path: String("/")}}
+		want := &createPagesRequest{BuildType: String("legacy"), Source: &PagesSource{Branch: String("master"), Path: String("/")}}
 		if !cmp.Equal(v, want) {
 			t.Errorf("Request body = %+v, want %+v", v, want)
 		}
 
-		fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h", "source": {"branch":"master", "path":"/"}}`)
+		fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h","build_type": "legacy","source": {"branch":"master", "path":"/"}}`)
 	})
 
 	ctx := context.Background()
@@ -49,7 +50,7 @@ func TestRepositoriesService_EnablePages(t *testing.T) {
 		t.Errorf("Repositories.EnablePages returned error: %v", err)
 	}
 
-	want := &Pages{URL: String("u"), Status: String("s"), CNAME: String("c"), Custom404: Bool(false), HTMLURL: String("h"), Source: &PagesSource{Branch: String("master"), Path: String("/")}}
+	want := &Pages{URL: String("u"), Status: String("s"), CNAME: String("c"), Custom404: Bool(false), HTMLURL: String("h"), BuildType: String("legacy"), Source: &PagesSource{Branch: String("master"), Path: String("/")}}
 
 	if !cmp.Equal(page, want) {
 		t.Errorf("Repositories.EnablePages returned %v, want %v", page, want)
@@ -70,13 +71,103 @@ func TestRepositoriesService_EnablePages(t *testing.T) {
 	})
 }
 
-func TestRepositoriesService_UpdatePages(t *testing.T) {
+func TestRepositoriesService_EnablePagesWorkflow(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &Pages{
+		BuildType: String("workflow"),
+		CNAME:     String("www.my-domain.com"), // not passed along.
+	}
+
+	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
+		v := new(createPagesRequest)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+		testHeader(t, r, "Accept", mediaTypeEnablePagesAPIPreview)
+		want := &createPagesRequest{BuildType: String("workflow")}
+		if !cmp.Equal(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h","build_type": "workflow"}`)
+	})
+
+	ctx := context.Background()
+	page, _, err := client.Repositories.EnablePages(ctx, "o", "r", input)
+	if err != nil {
+		t.Errorf("Repositories.EnablePages returned error: %v", err)
+	}
+
+	want := &Pages{URL: String("u"), Status: String("s"), CNAME: String("c"), Custom404: Bool(false), HTMLURL: String("h"), BuildType: String("workflow")}
+
+	if !cmp.Equal(page, want) {
+		t.Errorf("Repositories.EnablePages returned %v, want %v", page, want)
+	}
+
+	const methodName = "EnablePages"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.EnablePages(ctx, "\n", "\n", input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.EnablePages(ctx, "o", "r", input)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_UpdatePagesLegacy(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
 
 	input := &PagesUpdate{
-		CNAME:  String("www.my-domain.com"),
-		Source: &PagesSource{Branch: String("gh-pages")},
+		CNAME:     String("www.my-domain.com"),
+		BuildType: String("legacy"),
+		Source:    &PagesSource{Branch: String("gh-pages")},
+	}
+
+	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
+		v := new(PagesUpdate)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "PUT")
+		want := &PagesUpdate{CNAME: String("www.my-domain.com"), BuildType: String("legacy"), 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","build_type":"legacy","source":{"branch":"gh-pages"}}`)
+	})
+
+	ctx := context.Background()
+	_, err := client.Repositories.UpdatePages(ctx, "o", "r", input)
+	if err != nil {
+		t.Errorf("Repositories.UpdatePages returned error: %v", err)
+	}
+
+	const methodName = "UpdatePages"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Repositories.UpdatePages(ctx, "\n", "\n", input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Repositories.UpdatePages(ctx, "o", "r", input)
+	})
+}
+
+func TestRepositoriesService_UpdatePagesWorkflow(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &PagesUpdate{
+		CNAME:     String("www.my-domain.com"),
+		BuildType: String("workflow"),
 	}
 
 	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
@@ -84,12 +175,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: &PagesSource{Branch: String("gh-pages")}}
+		want := &PagesUpdate{CNAME: String("www.my-domain.com"), BuildType: String("workflow")}
 		if !cmp.Equal(v, want) {
 			t.Errorf("Request body = %+v, want %+v", v, want)
 		}
 
-		fmt.Fprint(w, `{"cname":"www.my-domain.com","source":{"branch":"gh-pages"}}`)
+		fmt.Fprint(w, `{"cname":"www.my-domain.com","build_type":"workflow"}`)
 	})
 
 	ctx := context.Background()
diff --git a/github/users_emails_test.go b/github/users_emails_test.go
index d40356d8..ad9d32e9 100644
--- a/github/users_emails_test.go
+++ b/github/users_emails_test.go
@@ -140,3 +140,47 @@ func TestUserEmail_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestUsersService_SetEmailVisibility(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &UserEmail{Visibility: String("private")}
+
+	mux.HandleFunc("/user/email/visibility", func(w http.ResponseWriter, r *http.Request) {
+		v := new(UserEmail)
+		json.NewDecoder(r.Body).Decode(&v)
+
+		testMethod(t, r, "PATCH")
+		if !cmp.Equal(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `[{
+			"email": "user@example.com",
+			"verified": false,
+			"primary": true,
+			"visibility": "private"
+		}]`)
+	})
+
+	ctx := context.Background()
+	emails, _, err := client.Users.SetEmailVisibility(ctx, "private")
+	if err != nil {
+		t.Errorf("Users.SetEmailVisibility returned error: %v", err)
+	}
+
+	want := []*UserEmail{{Email: String("user@example.com"), Verified: Bool(false), Primary: Bool(true), Visibility: String("private")}}
+	if !cmp.Equal(emails, want) {
+		t.Errorf("Users.SetEmailVisibility returned %+v, want %+v", emails, want)
+	}
+
+	const methodName = "SetEmailVisibility"
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Users.SetEmailVisibility(ctx, "private")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}