github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/run/list/list.go (about)

     1  package list
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/cli/cli/api"
     9  	"github.com/cli/cli/internal/ghrepo"
    10  	"github.com/cli/cli/pkg/cmd/run/shared"
    11  	workflowShared "github.com/cli/cli/pkg/cmd/workflow/shared"
    12  	"github.com/cli/cli/pkg/cmdutil"
    13  	"github.com/cli/cli/pkg/iostreams"
    14  	"github.com/cli/cli/utils"
    15  	"github.com/spf13/cobra"
    16  )
    17  
    18  const (
    19  	defaultLimit = 20
    20  )
    21  
    22  type ListOptions struct {
    23  	IO         *iostreams.IOStreams
    24  	HttpClient func() (*http.Client, error)
    25  	BaseRepo   func() (ghrepo.Interface, error)
    26  
    27  	PlainOutput bool
    28  
    29  	Limit            int
    30  	WorkflowSelector string
    31  }
    32  
    33  func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
    34  	opts := &ListOptions{
    35  		IO:         f.IOStreams,
    36  		HttpClient: f.HttpClient,
    37  	}
    38  
    39  	cmd := &cobra.Command{
    40  		Use:   "list",
    41  		Short: "List recent workflow runs",
    42  		Args:  cobra.NoArgs,
    43  		RunE: func(cmd *cobra.Command, args []string) error {
    44  			// support `-R, --repo` override
    45  			opts.BaseRepo = f.BaseRepo
    46  
    47  			terminal := opts.IO.IsStdoutTTY() && opts.IO.IsStdinTTY()
    48  			opts.PlainOutput = !terminal
    49  
    50  			if opts.Limit < 1 {
    51  				return &cmdutil.FlagError{Err: fmt.Errorf("invalid limit: %v", opts.Limit)}
    52  			}
    53  
    54  			if runF != nil {
    55  				return runF(opts)
    56  			}
    57  
    58  			return listRun(opts)
    59  		},
    60  	}
    61  
    62  	cmd.Flags().IntVarP(&opts.Limit, "limit", "L", defaultLimit, "Maximum number of runs to fetch")
    63  	cmd.Flags().StringVarP(&opts.WorkflowSelector, "workflow", "w", "", "Filter runs by workflow")
    64  
    65  	return cmd
    66  }
    67  
    68  func listRun(opts *ListOptions) error {
    69  	baseRepo, err := opts.BaseRepo()
    70  	if err != nil {
    71  		return fmt.Errorf("failed to determine base repo: %w", err)
    72  	}
    73  
    74  	c, err := opts.HttpClient()
    75  	if err != nil {
    76  		return fmt.Errorf("failed to create http client: %w", err)
    77  	}
    78  	client := api.NewClientFromHTTP(c)
    79  
    80  	var runs []shared.Run
    81  	var workflow *workflowShared.Workflow
    82  
    83  	opts.IO.StartProgressIndicator()
    84  	if opts.WorkflowSelector != "" {
    85  		states := []workflowShared.WorkflowState{workflowShared.Active}
    86  		workflow, err = workflowShared.ResolveWorkflow(
    87  			opts.IO, client, baseRepo, false, opts.WorkflowSelector, states)
    88  		if err == nil {
    89  			runs, err = shared.GetRunsByWorkflow(client, baseRepo, opts.Limit, workflow.ID)
    90  		}
    91  	} else {
    92  		runs, err = shared.GetRuns(client, baseRepo, opts.Limit)
    93  	}
    94  	opts.IO.StopProgressIndicator()
    95  	if err != nil {
    96  		return fmt.Errorf("failed to get runs: %w", err)
    97  	}
    98  
    99  	tp := utils.NewTablePrinter(opts.IO)
   100  
   101  	cs := opts.IO.ColorScheme()
   102  
   103  	if len(runs) == 0 {
   104  		if !opts.PlainOutput {
   105  			fmt.Fprintln(opts.IO.ErrOut, "No runs found")
   106  		}
   107  		return nil
   108  	}
   109  
   110  	out := opts.IO.Out
   111  
   112  	if !opts.PlainOutput {
   113  		tp.AddField("STATUS", nil, nil)
   114  		tp.AddField("NAME", nil, nil)
   115  		tp.AddField("WORKFLOW", nil, nil)
   116  		tp.AddField("BRANCH", nil, nil)
   117  		tp.AddField("EVENT", nil, nil)
   118  		tp.AddField("ID", nil, nil)
   119  		tp.AddField("ELAPSED", nil, nil)
   120  		tp.AddField("AGE", nil, nil)
   121  		tp.EndRow()
   122  	}
   123  
   124  	for _, run := range runs {
   125  		if opts.PlainOutput {
   126  			tp.AddField(string(run.Status), nil, nil)
   127  			tp.AddField(string(run.Conclusion), nil, nil)
   128  		} else {
   129  			symbol, symbolColor := shared.Symbol(cs, run.Status, run.Conclusion)
   130  			tp.AddField(symbol, nil, symbolColor)
   131  		}
   132  
   133  		tp.AddField(run.CommitMsg(), nil, cs.Bold)
   134  
   135  		tp.AddField(run.Name, nil, nil)
   136  		tp.AddField(run.HeadBranch, nil, cs.Bold)
   137  		tp.AddField(string(run.Event), nil, nil)
   138  		tp.AddField(fmt.Sprintf("%d", run.ID), nil, cs.Cyan)
   139  
   140  		elapsed := run.UpdatedAt.Sub(run.CreatedAt)
   141  		if elapsed < 0 {
   142  			elapsed = 0
   143  		}
   144  		tp.AddField(elapsed.String(), nil, nil)
   145  		tp.AddField(utils.FuzzyAgoAbbr(time.Now(), run.CreatedAt), nil, nil)
   146  		tp.EndRow()
   147  	}
   148  
   149  	err = tp.Render()
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	if !opts.PlainOutput {
   155  		fmt.Fprintln(out)
   156  		fmt.Fprintln(out, "For details on a run, try: gh run view <run-id>")
   157  	}
   158  
   159  	return nil
   160  }