Repository: google/go-github (go)

Issue:
Add support for getting a workflow run attempt
I would like to add support for getting a workflow run by specifying the attempt number. From this doc:
https://docs.github.com/en/rest/reference/actions#get-a-workflow-run-attempt

I would like to ask about the naming convention for the method. I was considering either `GetWorkflowRunByAttempt` or `GetWorkflowRunByIDByAttempt`. Could someone please help me with the appropriate name?

This is my initial proposed method:
`GetWorkflowRunByAttempt(ctx context.Context, owner, repo string, runID int64, attemptNumber int) (*WorkflowRun, *Response, error)`

I started working on the code changes for this so this issue can be assigned to me.
Thank you!!

Pull request description:
Add GetWorkflowRunAttempt
Fixes: #2287 

Patch:
diff --git a/github/actions_workflow_runs.go b/github/actions_workflow_runs.go
index 1e1ff61a..273d2cc4 100644
--- a/github/actions_workflow_runs.go
+++ b/github/actions_workflow_runs.go
@@ -88,6 +88,11 @@ type WorkflowRunJobRun struct {
 	DurationMS *int64 `json:"duration_ms,omitempty"`
 }
 
+// WorkflowRunAttemptOptions specifies optional parameters to GetWorkflowRunAttempt.
+type WorkflowRunAttemptOptions struct {
+	ExcludePullRequests *bool `url:"exclude_pull_requests,omitempty"`
+}
+
 func (s *ActionsService) listWorkflowRuns(ctx context.Context, endpoint string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) {
 	u, err := addOptions(endpoint, opts)
 	if err != nil {
@@ -168,6 +173,30 @@ func (s *ActionsService) GetWorkflowRunByID(ctx context.Context, owner, repo str
 	return run, resp, nil
 }
 
+// GetWorkflowRunAttempt gets a specific workflow run attempt.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-a-workflow-run-attempt
+func (s *ActionsService) GetWorkflowRunAttempt(ctx context.Context, owner, repo string, runID int64, attemptNumber int, opts *WorkflowRunAttemptOptions) (*WorkflowRun, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/attempts/%v", owner, repo, runID, attemptNumber)
+	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
+	}
+
+	run := new(WorkflowRun)
+	resp, err := s.client.Do(ctx, req, run)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return run, resp, nil
+}
+
 // RerunWorkflowByID re-runs a workflow by ID.
 //
 // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#re-run-a-workflow
diff --git a/github/github-accessors.go b/github/github-accessors.go
index c80a2c2c..3be5bdd3 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -19564,6 +19564,14 @@ func (w *WorkflowRun) GetWorkflowURL() string {
 	return *w.WorkflowURL
 }
 
+// GetExcludePullRequests returns the ExcludePullRequests field if it's non-nil, zero value otherwise.
+func (w *WorkflowRunAttemptOptions) GetExcludePullRequests() bool {
+	if w == nil || w.ExcludePullRequests == nil {
+		return false
+	}
+	return *w.ExcludePullRequests
+}
+
 // GetJobs returns the Jobs field if it's non-nil, zero value otherwise.
 func (w *WorkflowRunBill) GetJobs() int {
 	if w == nil || w.Jobs == nil {


Test patch:
diff --git a/github/actions_workflow_runs_test.go b/github/actions_workflow_runs_test.go
index 7ebdbf58..0b38ae38 100644
--- a/github/actions_workflow_runs_test.go
+++ b/github/actions_workflow_runs_test.go
@@ -143,6 +143,50 @@ func TestActionsService_GetWorkflowRunByID(t *testing.T) {
 	})
 }
 
+func TestActionsService_GetWorkflowRunAttempt(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/runs/29679449/attempts/3", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testFormValues(t, r, values{"exclude_pull_requests": "true"})
+		fmt.Fprint(w, `{"id":399444496,"run_number":296,"run_attempt":3,"created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}}`)
+	})
+
+	opts := &WorkflowRunAttemptOptions{ExcludePullRequests: Bool(true)}
+	ctx := context.Background()
+	runs, _, err := client.Actions.GetWorkflowRunAttempt(ctx, "o", "r", 29679449, 3, opts)
+	if err != nil {
+		t.Errorf("Actions.GetWorkflowRunAttempt returned error: %v", err)
+	}
+
+	want := &WorkflowRun{
+		ID:         Int64(399444496),
+		RunNumber:  Int(296),
+		RunAttempt: Int(3),
+		CreatedAt:  &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)},
+		UpdatedAt:  &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)},
+	}
+
+	if !cmp.Equal(runs, want) {
+		t.Errorf("Actions.GetWorkflowRunAttempt returned %+v, want %+v", runs, want)
+	}
+
+	const methodName = "GetWorkflowRunAttempt"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.GetWorkflowRunAttempt(ctx, "\n", "\n", 29679449, 3, opts)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.GetWorkflowRunAttempt(ctx, "o", "r", 29679449, 3, opts)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
 func TestActionsService_RerunWorkflowRunByID(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index c8df83ad..986b8c7e 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -22902,6 +22902,16 @@ func TestWorkflowRun_GetWorkflowURL(tt *testing.T) {
 	w.GetWorkflowURL()
 }
 
+func TestWorkflowRunAttemptOptions_GetExcludePullRequests(tt *testing.T) {
+	var zeroValue bool
+	w := &WorkflowRunAttemptOptions{ExcludePullRequests: &zeroValue}
+	w.GetExcludePullRequests()
+	w = &WorkflowRunAttemptOptions{}
+	w.GetExcludePullRequests()
+	w = nil
+	w.GetExcludePullRequests()
+}
+
 func TestWorkflowRunBill_GetJobs(tt *testing.T) {
 	var zeroValue int
 	w := &WorkflowRunBill{Jobs: &zeroValue}