Add branch and actor filters to run list (#4100)
* Add branch and actor filters to `run list` * Simplify what FilterOptions can do * Check not only limit in TestNewCmdList * Verify that branch/actor params are parsed properly * Verify that API requests have proper query parameters * Change flag name from actor to user Co-authored-by: Sam Coe <samcoe@users.noreply.github.com>
This commit is contained in:
parent
a564909f03
commit
e28fa3c112
7 changed files with 107 additions and 13 deletions
|
|
@ -74,7 +74,7 @@ func runCancel(opts *CancelOptions) error {
|
|||
var run *shared.Run
|
||||
|
||||
if opts.Prompt {
|
||||
runs, err := shared.GetRunsWithFilter(client, repo, 10, func(run shared.Run) bool {
|
||||
runs, err := shared.GetRunsWithFilter(client, repo, nil, 10, func(run shared.Run) bool {
|
||||
return run.Status != shared.Completed
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ type ListOptions struct {
|
|||
|
||||
Limit int
|
||||
WorkflowSelector string
|
||||
Branch string
|
||||
Actor string
|
||||
}
|
||||
|
||||
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
|
||||
|
|
@ -62,6 +64,8 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
|
|||
|
||||
cmd.Flags().IntVarP(&opts.Limit, "limit", "L", defaultLimit, "Maximum number of runs to fetch")
|
||||
cmd.Flags().StringVarP(&opts.WorkflowSelector, "workflow", "w", "", "Filter runs by workflow")
|
||||
cmd.Flags().StringVarP(&opts.Branch, "branch", "b", "", "Filter runs by branch")
|
||||
cmd.Flags().StringVarP(&opts.Actor, "user", "u", "", "Filter runs by user who triggered the run")
|
||||
cmdutil.AddJSONFlags(cmd, &opts.Exporter, shared.RunFields)
|
||||
|
||||
return cmd
|
||||
|
|
@ -82,16 +86,21 @@ func listRun(opts *ListOptions) error {
|
|||
var runs []shared.Run
|
||||
var workflow *workflowShared.Workflow
|
||||
|
||||
filters := &shared.FilterOptions{
|
||||
Branch: opts.Branch,
|
||||
Actor: opts.Actor,
|
||||
}
|
||||
|
||||
opts.IO.StartProgressIndicator()
|
||||
if opts.WorkflowSelector != "" {
|
||||
states := []workflowShared.WorkflowState{workflowShared.Active}
|
||||
workflow, err = workflowShared.ResolveWorkflow(
|
||||
opts.IO, client, baseRepo, false, opts.WorkflowSelector, states)
|
||||
if err == nil {
|
||||
runs, err = shared.GetRunsByWorkflow(client, baseRepo, opts.Limit, workflow.ID)
|
||||
runs, err = shared.GetRunsByWorkflow(client, baseRepo, filters, opts.Limit, workflow.ID)
|
||||
}
|
||||
} else {
|
||||
runs, err = shared.GetRuns(client, baseRepo, opts.Limit)
|
||||
runs, err = shared.GetRuns(client, baseRepo, filters, opts.Limit)
|
||||
}
|
||||
opts.IO.StopProgressIndicator()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
|
|
@ -51,6 +52,22 @@ func TestNewCmdList(t *testing.T) {
|
|||
WorkflowSelector: "foo.yml",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "branch",
|
||||
cli: "--branch new-cool-stuff",
|
||||
wants: ListOptions{
|
||||
Limit: defaultLimit,
|
||||
Branch: "new-cool-stuff",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user",
|
||||
cli: "--user bak1an",
|
||||
wants: ListOptions{
|
||||
Limit: defaultLimit,
|
||||
Actor: "bak1an",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -83,11 +100,32 @@ func TestNewCmdList(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.Equal(t, tt.wants.Limit, gotOpts.Limit)
|
||||
assert.Equal(t, tt.wants.WorkflowSelector, gotOpts.WorkflowSelector)
|
||||
assert.Equal(t, tt.wants.Branch, gotOpts.Branch)
|
||||
assert.Equal(t, tt.wants.Actor, gotOpts.Actor)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRun(t *testing.T) {
|
||||
// helper to match mocked requests by their query params along with method and path
|
||||
queryMatcher := func(method string, path string, query url.Values) httpmock.Matcher {
|
||||
return func(req *http.Request) bool {
|
||||
if !httpmock.REST(method, path)(req) {
|
||||
return false
|
||||
}
|
||||
|
||||
actualQuery := req.URL.Query()
|
||||
|
||||
for param := range query {
|
||||
if !(actualQuery.Get(param) == query.Get(param)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
opts *ListOptions
|
||||
|
|
@ -198,6 +236,40 @@ func TestListRun(t *testing.T) {
|
|||
},
|
||||
wantOut: "STATUS NAME WORKFLOW BRANCH EVENT ID ELAPSED AGE\n* cool commit in progress trunk push 2 4m34s Feb 23, 2021\n✓ cool commit successful trunk push 3 4m34s Feb 23, 2021\nX cool commit failed trunk push 1234 4m34s Feb 23, 2021\n\nFor details on a run, try: gh run view <run-id>\n",
|
||||
},
|
||||
{
|
||||
name: "branch filter applied",
|
||||
opts: &ListOptions{
|
||||
Limit: defaultLimit,
|
||||
Branch: "the-branch",
|
||||
},
|
||||
stubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
queryMatcher("GET", "repos/OWNER/REPO/actions/runs", url.Values{
|
||||
"branch": []string{"the-branch"},
|
||||
}),
|
||||
httpmock.JSONResponse(shared.RunsPayload{}),
|
||||
)
|
||||
},
|
||||
wantOut: "",
|
||||
wantErrOut: "No runs found\n",
|
||||
},
|
||||
{
|
||||
name: "actor filter applied",
|
||||
opts: &ListOptions{
|
||||
Limit: defaultLimit,
|
||||
Actor: "bak1an",
|
||||
},
|
||||
stubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
queryMatcher("GET", "repos/OWNER/REPO/actions/runs", url.Values{
|
||||
"actor": []string{"bak1an"},
|
||||
}),
|
||||
httpmock.JSONResponse(shared.RunsPayload{}),
|
||||
)
|
||||
},
|
||||
wantOut: "",
|
||||
wantErrOut: "No runs found\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ func runRerun(opts *RerunOptions) error {
|
|||
|
||||
if opts.Prompt {
|
||||
cs := opts.IO.ColorScheme()
|
||||
runs, err := shared.GetRunsWithFilter(client, repo, 10, func(run shared.Run) bool {
|
||||
runs, err := shared.GetRunsWithFilter(client, repo, nil, 10, func(run shared.Run) bool {
|
||||
if run.Status != shared.Completed {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,9 +205,14 @@ type RunsPayload struct {
|
|||
WorkflowRuns []Run `json:"workflow_runs"`
|
||||
}
|
||||
|
||||
func GetRunsWithFilter(client *api.Client, repo ghrepo.Interface, limit int, f func(Run) bool) ([]Run, error) {
|
||||
type FilterOptions struct {
|
||||
Branch string
|
||||
Actor string
|
||||
}
|
||||
|
||||
func GetRunsWithFilter(client *api.Client, repo ghrepo.Interface, opts *FilterOptions, limit int, f func(Run) bool) ([]Run, error) {
|
||||
path := fmt.Sprintf("repos/%s/actions/runs", ghrepo.FullName(repo))
|
||||
runs, err := getRuns(client, repo, path, 50)
|
||||
runs, err := getRuns(client, repo, path, opts, 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -224,17 +229,17 @@ func GetRunsWithFilter(client *api.Client, repo ghrepo.Interface, limit int, f f
|
|||
return filtered, nil
|
||||
}
|
||||
|
||||
func GetRunsByWorkflow(client *api.Client, repo ghrepo.Interface, limit int, workflowID int64) ([]Run, error) {
|
||||
func GetRunsByWorkflow(client *api.Client, repo ghrepo.Interface, opts *FilterOptions, limit int, workflowID int64) ([]Run, error) {
|
||||
path := fmt.Sprintf("repos/%s/actions/workflows/%d/runs", ghrepo.FullName(repo), workflowID)
|
||||
return getRuns(client, repo, path, limit)
|
||||
return getRuns(client, repo, path, opts, limit)
|
||||
}
|
||||
|
||||
func GetRuns(client *api.Client, repo ghrepo.Interface, limit int) ([]Run, error) {
|
||||
func GetRuns(client *api.Client, repo ghrepo.Interface, opts *FilterOptions, limit int) ([]Run, error) {
|
||||
path := fmt.Sprintf("repos/%s/actions/runs", ghrepo.FullName(repo))
|
||||
return getRuns(client, repo, path, limit)
|
||||
return getRuns(client, repo, path, opts, limit)
|
||||
}
|
||||
|
||||
func getRuns(client *api.Client, repo ghrepo.Interface, path string, limit int) ([]Run, error) {
|
||||
func getRuns(client *api.Client, repo ghrepo.Interface, path string, opts *FilterOptions, limit int) ([]Run, error) {
|
||||
perPage := limit
|
||||
page := 1
|
||||
if limit > 100 {
|
||||
|
|
@ -253,6 +258,14 @@ func getRuns(client *api.Client, repo ghrepo.Interface, path string, limit int)
|
|||
query := parsed.Query()
|
||||
query.Set("per_page", fmt.Sprintf("%d", perPage))
|
||||
query.Set("page", fmt.Sprintf("%d", page))
|
||||
if opts != nil {
|
||||
if opts.Branch != "" {
|
||||
query.Set("branch", opts.Branch)
|
||||
}
|
||||
if opts.Actor != "" {
|
||||
query.Set("actor", opts.Actor)
|
||||
}
|
||||
}
|
||||
parsed.RawQuery = query.Encode()
|
||||
pagedPath := parsed.String()
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ func runView(opts *ViewOptions) error {
|
|||
if opts.Prompt {
|
||||
// TODO arbitrary limit
|
||||
opts.IO.StartProgressIndicator()
|
||||
runs, err := shared.GetRuns(client, repo, 10)
|
||||
runs, err := shared.GetRuns(client, repo, nil, 10)
|
||||
opts.IO.StopProgressIndicator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get runs: %w", err)
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ func watchRun(opts *WatchOptions) error {
|
|||
var run *shared.Run
|
||||
|
||||
if opts.Prompt {
|
||||
runs, err := shared.GetRunsWithFilter(client, repo, 10, func(run shared.Run) bool {
|
||||
runs, err := shared.GetRunsWithFilter(client, repo, nil, 10, func(run shared.Run) bool {
|
||||
return run.Status != shared.Completed
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue