cli/pkg/cmd/project/list/list.go
Heath Stewart 0cf5d22ead
Support template, jq flags with standard format flag (#7832)
* Use standard JSON flags for project command

Deprecates the --format flag and adds the standard JSON flags to properly filter, template, and write JSON.

* Add format flags cmdutil

Resolves PR feedback

* Remove unnecessary fields from JSON format flags

* Add standard format help to remaining commands

* Add JSON format regression tests

Also fixed a number of `project` commands that didn't format the right object as JSON.

* Resolve PR feedback
2024-01-09 09:46:39 -08:00

180 lines
4.5 KiB
Go

package list
import (
"fmt"
"strconv"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/pkg/cmd/project/shared/client"
"github.com/cli/cli/v2/pkg/cmd/project/shared/format"
"github.com/cli/cli/v2/pkg/cmd/project/shared/queries"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/spf13/cobra"
)
type listOpts struct {
limit int
web bool
owner string
closed bool
exporter cmdutil.Exporter
}
type listConfig struct {
client *queries.Client
opts listOpts
URLOpener func(string) error
io *iostreams.IOStreams
}
func NewCmdList(f *cmdutil.Factory, runF func(config listConfig) error) *cobra.Command {
opts := listOpts{}
listCmd := &cobra.Command{
Use: "list",
Short: "List the projects for an owner",
Example: heredoc.Doc(`
# list the current user's projects
gh project list
# list the projects for org github including closed projects
gh project list --owner github --closed
`),
Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error {
client, err := client.New(f)
if err != nil {
return err
}
URLOpener := func(url string) error {
return f.Browser.Browse(url)
}
config := listConfig{
client: client,
opts: opts,
URLOpener: URLOpener,
io: f.IOStreams,
}
// allow testing of the command without actually running it
if runF != nil {
return runF(config)
}
return runList(config)
},
}
listCmd.Flags().StringVar(&opts.owner, "owner", "", "Login of the owner")
listCmd.Flags().BoolVarP(&opts.closed, "closed", "", false, "Include closed projects")
listCmd.Flags().BoolVarP(&opts.web, "web", "w", false, "Open projects list in the browser")
cmdutil.AddFormatFlags(listCmd, &opts.exporter)
listCmd.Flags().IntVarP(&opts.limit, "limit", "L", queries.LimitDefault, "Maximum number of projects to fetch")
return listCmd
}
func runList(config listConfig) error {
if config.opts.web {
url, err := buildURL(config)
if err != nil {
return err
}
if err := config.URLOpener(url); err != nil {
return err
}
return nil
}
if config.opts.owner == "" {
config.opts.owner = "@me"
}
canPrompt := config.io.CanPrompt()
owner, err := config.client.NewOwner(canPrompt, config.opts.owner)
if err != nil {
return err
}
projects, totalCount, err := config.client.Projects(config.opts.owner, owner.Type, config.opts.limit, false)
if err != nil {
return err
}
projects = filterProjects(projects, config)
if config.opts.exporter != nil {
return printJSON(config, projects, totalCount)
}
return printResults(config, projects, owner.Login)
}
// TODO: support non-github.com hostnames
func buildURL(config listConfig) (string, error) {
var url string
if config.opts.owner == "@me" || config.opts.owner == "" {
owner, err := config.client.ViewerLoginName()
if err != nil {
return "", err
}
url = fmt.Sprintf("https://github.com/users/%s/projects", owner)
} else {
_, ownerType, err := config.client.OwnerIDAndType(config.opts.owner)
if err != nil {
return "", err
}
if ownerType == queries.UserOwner {
url = fmt.Sprintf("https://github.com/users/%s/projects", config.opts.owner)
} else {
url = fmt.Sprintf("https://github.com/orgs/%s/projects", config.opts.owner)
}
}
if config.opts.closed {
return url + "?query=is%3Aclosed", nil
}
return url, nil
}
func filterProjects(nodes []queries.Project, config listConfig) []queries.Project {
projects := make([]queries.Project, 0, len(nodes))
for _, p := range nodes {
if !config.opts.closed && p.Closed {
continue
}
projects = append(projects, p)
}
return projects
}
func printResults(config listConfig, projects []queries.Project, owner string) error {
if len(projects) == 0 {
return cmdutil.NewNoResultsError(fmt.Sprintf("No projects found for %s", owner))
}
tp := tableprinter.New(config.io, tableprinter.WithHeader("Number", "Title", "State", "ID"))
cs := config.io.ColorScheme()
for _, p := range projects {
tp.AddField(
strconv.Itoa(int(p.Number)),
tableprinter.WithTruncate(nil),
)
tp.AddField(p.Title)
tp.AddField(
format.ProjectState(p),
tableprinter.WithColor(cs.ColorFromString(format.ColorForProjectState(p))),
)
tp.AddField(p.ID, tableprinter.WithTruncate(nil))
tp.EndRow()
}
return tp.Render()
}
func printJSON(config listConfig, projects []queries.Project, totalCount int) error {
projectsJSON := format.JSONProjects(projects, totalCount)
return config.opts.exporter.Write(config.io, projectsJSON)
}