Skip to content

Commit 8480381

Browse files
author
Nate Smith
authored
workflow list (cli#3245)
* add gh workflow list * review feedback
1 parent 126b498 commit 8480381

File tree

4 files changed

+438
-0
lines changed

4 files changed

+438
-0
lines changed

pkg/cmd/root/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
secretCmd "github.com/cli/cli/pkg/cmd/secret"
2626
sshKeyCmd "github.com/cli/cli/pkg/cmd/ssh-key"
2727
versionCmd "github.com/cli/cli/pkg/cmd/version"
28+
workflowCmd "github.com/cli/cli/pkg/cmd/workflow"
2829
"github.com/cli/cli/pkg/cmdutil"
2930
"github.com/spf13/cobra"
3031
)
@@ -85,6 +86,7 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) *cobra.Command {
8586
cmd.AddCommand(actionsCmd.NewCmdActions(f))
8687
cmd.AddCommand(runCmd.NewCmdRun(f))
8788
cmd.AddCommand(jobCmd.NewCmdJob(f))
89+
cmd.AddCommand(workflowCmd.NewCmdWorkflow(f))
8890

8991
// the `api` command should not inherit any extra HTTP headers
9092
bareHTTPCmdFactory := *f

pkg/cmd/workflow/list/list.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package list
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/cli/cli/api"
8+
"github.com/cli/cli/internal/ghrepo"
9+
"github.com/cli/cli/pkg/cmdutil"
10+
"github.com/cli/cli/pkg/iostreams"
11+
"github.com/cli/cli/utils"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
const (
16+
defaultLimit = 10
17+
18+
Active WorkflowState = "active"
19+
DisabledManually WorkflowState = "disabled_manually"
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+
All bool
30+
Limit int
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 GitHub Actions workflows",
42+
Args: cobra.NoArgs,
43+
Hidden: true,
44+
RunE: func(cmd *cobra.Command, args []string) error {
45+
// support `-R, --repo` override
46+
opts.BaseRepo = f.BaseRepo
47+
48+
terminal := opts.IO.IsStdoutTTY() && opts.IO.IsStdinTTY()
49+
opts.PlainOutput = !terminal
50+
51+
if opts.Limit < 1 {
52+
return &cmdutil.FlagError{Err: fmt.Errorf("invalid limit: %v", opts.Limit)}
53+
}
54+
55+
if runF != nil {
56+
return runF(opts)
57+
}
58+
59+
return listRun(opts)
60+
},
61+
}
62+
63+
cmd.Flags().IntVarP(&opts.Limit, "limit", "L", defaultLimit, "Maximum number of workflows to fetch")
64+
cmd.Flags().BoolVarP(&opts.All, "all", "a", false, "Show all workflows, including disabled workflows")
65+
66+
return cmd
67+
}
68+
69+
func listRun(opts *ListOptions) error {
70+
repo, err := opts.BaseRepo()
71+
if err != nil {
72+
return fmt.Errorf("could not determine base repo: %w", err)
73+
}
74+
75+
httpClient, err := opts.HttpClient()
76+
if err != nil {
77+
return fmt.Errorf("could not create http client: %w", err)
78+
}
79+
client := api.NewClientFromHTTP(httpClient)
80+
81+
opts.IO.StartProgressIndicator()
82+
workflows, err := getWorkflows(client, repo, opts.Limit)
83+
opts.IO.StopProgressIndicator()
84+
if err != nil {
85+
return fmt.Errorf("could not get workflows: %w", err)
86+
}
87+
88+
if len(workflows) == 0 {
89+
if !opts.PlainOutput {
90+
fmt.Fprintln(opts.IO.ErrOut, "No workflows found")
91+
}
92+
return nil
93+
}
94+
95+
tp := utils.NewTablePrinter(opts.IO)
96+
cs := opts.IO.ColorScheme()
97+
98+
for _, workflow := range workflows {
99+
if workflow.Disabled() && !opts.All {
100+
continue
101+
}
102+
tp.AddField(workflow.Name, nil, cs.Bold)
103+
tp.AddField(string(workflow.State), nil, nil)
104+
tp.AddField(fmt.Sprintf("%d", workflow.ID), nil, cs.Cyan)
105+
tp.EndRow()
106+
}
107+
108+
return tp.Render()
109+
}
110+
111+
type WorkflowState string
112+
113+
type Workflow struct {
114+
Name string
115+
ID int
116+
State WorkflowState
117+
}
118+
119+
func (w *Workflow) Disabled() bool {
120+
return w.State != Active
121+
}
122+
123+
type WorkflowsPayload struct {
124+
Workflows []Workflow
125+
}
126+
127+
func getWorkflows(client *api.Client, repo ghrepo.Interface, limit int) ([]Workflow, error) {
128+
perPage := limit
129+
page := 1
130+
if limit > 100 {
131+
perPage = 100
132+
}
133+
134+
workflows := []Workflow{}
135+
136+
for len(workflows) < limit {
137+
var result WorkflowsPayload
138+
139+
path := fmt.Sprintf("repos/%s/actions/workflows?per_page=%d&page=%d", ghrepo.FullName(repo), perPage, page)
140+
141+
err := client.REST(repo.RepoHost(), "GET", path, nil, &result)
142+
if err != nil {
143+
return nil, err
144+
}
145+
146+
for _, workflow := range result.Workflows {
147+
workflows = append(workflows, workflow)
148+
if len(workflows) == limit {
149+
break
150+
}
151+
}
152+
153+
if len(result.Workflows) < perPage {
154+
break
155+
}
156+
157+
page++
158+
}
159+
160+
return workflows, nil
161+
}

0 commit comments

Comments
 (0)