Merge pull request #11621 from cli/kw/1004-gh-agent-task-list-respects--w--web
`gh agent-task list`: implement `-w`/`--web` to open global agent tasks page in a browser
This commit is contained in:
commit
fca5e0cd63
2 changed files with 95 additions and 28 deletions
|
|
@ -5,9 +5,11 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/tableprinter"
|
||||
"github.com/cli/cli/v2/internal/text"
|
||||
"github.com/cli/cli/v2/pkg/cmd/agent-task/capi"
|
||||
"github.com/cli/cli/v2/pkg/cmd/agent-task/shared"
|
||||
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
|
||||
|
|
@ -25,14 +27,17 @@ type ListOptions struct {
|
|||
Limit int
|
||||
CapiClient func() (*capi.CAPIClient, error)
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Web bool
|
||||
Browser browser.Browser
|
||||
}
|
||||
|
||||
// NewCmdList creates the list command
|
||||
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
|
||||
opts := &ListOptions{
|
||||
IO: f.IOStreams,
|
||||
Config: f.Config,
|
||||
Limit: defaultLimit,
|
||||
IO: f.IOStreams,
|
||||
Config: f.Config,
|
||||
Limit: defaultLimit,
|
||||
Browser: f.Browser,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
@ -41,9 +46,9 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
|
|||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Support -R/--repo override
|
||||
if f != nil {
|
||||
opts.BaseRepo = f.BaseRepo
|
||||
}
|
||||
|
||||
opts.BaseRepo = f.BaseRepo
|
||||
|
||||
if opts.Limit < 1 {
|
||||
return cmdutil.FlagErrorf("invalid limit: %v", opts.Limit)
|
||||
}
|
||||
|
|
@ -59,6 +64,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
|
|||
}
|
||||
|
||||
cmd.Flags().IntVarP(&opts.Limit, "limit", "L", defaultLimit, fmt.Sprintf("Maximum number of agent tasks to fetch (default %d)", defaultLimit))
|
||||
cmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Open agent tasks in the browser")
|
||||
|
||||
opts.CapiClient = func() (*capi.CAPIClient, error) {
|
||||
cfg, err := opts.Config()
|
||||
|
|
@ -77,6 +83,18 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
|
|||
}
|
||||
|
||||
func listRun(opts *ListOptions) error {
|
||||
if opts.Web {
|
||||
// Currently the web GUI does not have a page that supports filtering
|
||||
// based on repo, so we just open the agents dashboard with no args.
|
||||
// If that page is ever added in the future, we should route to that
|
||||
// page instead of the global one when --repo is set.
|
||||
const webURL = "https://github.com/copilot/agents"
|
||||
if opts.IO.IsStdoutTTY() {
|
||||
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", text.DisplayURL(webURL))
|
||||
}
|
||||
return opts.Browser.Browse(webURL)
|
||||
}
|
||||
|
||||
if opts.Limit <= 0 {
|
||||
opts.Limit = defaultLimit
|
||||
}
|
||||
|
|
@ -93,6 +111,8 @@ func listRun(opts *ListOptions) error {
|
|||
|
||||
var repo ghrepo.Interface
|
||||
if opts.BaseRepo != nil {
|
||||
// We swallow this error because when CWD is not a repo and
|
||||
// the --repo flag is not set, we use the global/user session listing.
|
||||
repo, _ = opts.BaseRepo()
|
||||
}
|
||||
|
||||
|
|
@ -110,8 +130,7 @@ func listRun(opts *ListOptions) error {
|
|||
opts.IO.StopProgressIndicator()
|
||||
|
||||
if len(sessions) == 0 {
|
||||
fmt.Fprintln(opts.IO.Out, "no agent tasks found")
|
||||
return nil
|
||||
return cmdutil.NewNoResultsError("no agent tasks found")
|
||||
}
|
||||
|
||||
cs := opts.IO.ColorScheme()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
|
|
@ -44,6 +45,19 @@ func TestNewCmdList(t *testing.T) {
|
|||
args: "--limit 0",
|
||||
wantErr: "invalid limit: 0",
|
||||
},
|
||||
{
|
||||
name: "negative limit",
|
||||
args: "--limit -5",
|
||||
wantErr: "invalid limit: -5",
|
||||
},
|
||||
{
|
||||
name: "web flag",
|
||||
args: "--web",
|
||||
wantOpts: ListOptions{
|
||||
Limit: defaultLimit,
|
||||
Web: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -62,30 +76,32 @@ func TestNewCmdList(t *testing.T) {
|
|||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.wantOpts.Limit, gotOpts.Limit)
|
||||
assert.Equal(t, tt.wantOpts.Web, gotOpts.Web)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listRun(t *testing.T) {
|
||||
sixHours, _ := time.ParseDuration("6h")
|
||||
sixHoursAgo := time.Now().Add(-sixHours)
|
||||
createdAt := sixHoursAgo.Format(time.RFC3339)
|
||||
createdAt := time.Now().Add(-6 * time.Hour).Format(time.RFC3339) // 6h ago
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tty bool
|
||||
stubs func(*httpmock.Registry)
|
||||
baseRepo ghrepo.Interface
|
||||
baseRepoErr error
|
||||
limit int
|
||||
wantOut string
|
||||
wantErr error
|
||||
name string
|
||||
tty bool
|
||||
stubs func(*httpmock.Registry)
|
||||
baseRepo ghrepo.Interface
|
||||
baseRepoErr error
|
||||
limit int
|
||||
web bool
|
||||
wantOut string
|
||||
wantErr error
|
||||
wantStderr string
|
||||
wantBrowserURL string
|
||||
}{
|
||||
{
|
||||
name: "no sessions",
|
||||
tty: true,
|
||||
stubs: func(reg *httpmock.Registry) { registerEmptySessionsMock(reg) },
|
||||
wantOut: "no agent tasks found\n",
|
||||
wantErr: cmdutil.NewNoResultsError("no agent tasks found"),
|
||||
},
|
||||
{
|
||||
name: "limit truncates sessions",
|
||||
|
|
@ -143,15 +159,14 @@ func Test_listRun(t *testing.T) {
|
|||
tty: true,
|
||||
stubs: func(reg *httpmock.Registry) { registerRepoEmptySessionsMock(reg, "OWNER", "REPO") },
|
||||
baseRepo: ghrepo.New("OWNER", "REPO"),
|
||||
wantOut: "no agent tasks found\n",
|
||||
wantErr: cmdutil.NewNoResultsError("no agent tasks found"),
|
||||
},
|
||||
{
|
||||
name: "repo resolution error does not surface",
|
||||
tty: true,
|
||||
baseRepoErr: errors.New("ambiguous repo"),
|
||||
wantErr: nil,
|
||||
wantErr: cmdutil.NewNoResultsError("no agent tasks found"),
|
||||
stubs: func(reg *httpmock.Registry) { registerEmptySessionsMock(reg) },
|
||||
wantOut: "no agent tasks found\n",
|
||||
},
|
||||
{
|
||||
name: "repo scoped many sessions (tty)",
|
||||
|
|
@ -168,6 +183,23 @@ func Test_listRun(t *testing.T) {
|
|||
r6 #306 OWNER/REPO mystery about 6 hours ago
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "web mode",
|
||||
tty: true,
|
||||
web: true,
|
||||
wantOut: "",
|
||||
wantStderr: "Opening https://github.com/copilot/agents in your browser.\n",
|
||||
wantBrowserURL: "https://github.com/copilot/agents",
|
||||
},
|
||||
{
|
||||
name: "web mode with repo still uses global URL, even when --repo is set",
|
||||
tty: true,
|
||||
web: true,
|
||||
baseRepo: ghrepo.New("OWNER", "REPO"),
|
||||
wantOut: "",
|
||||
wantStderr: "Opening https://github.com/copilot/agents in your browser.\n",
|
||||
wantBrowserURL: "https://github.com/copilot/agents",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -181,16 +213,28 @@ func Test_listRun(t *testing.T) {
|
|||
cfg.Set("github.com", "oauth_token", "OTOKEN")
|
||||
authCfg := cfg.Authentication()
|
||||
|
||||
ios, _, stdout, _ := iostreams.Test()
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
ios.SetStdoutTTY(tt.tty)
|
||||
|
||||
var br *browser.Stub
|
||||
if tt.web {
|
||||
br = &browser.Stub{}
|
||||
}
|
||||
|
||||
httpClient := &http.Client{Transport: reg}
|
||||
capiClient := capi.NewCAPIClient(httpClient, authCfg)
|
||||
opts := &ListOptions{
|
||||
IO: ios,
|
||||
Config: func() (gh.Config, error) { return cfg, nil },
|
||||
Limit: tt.limit,
|
||||
CapiClient: func() (*capi.CAPIClient, error) { return capiClient, nil },
|
||||
IO: ios,
|
||||
Config: func() (gh.Config, error) { return cfg, nil },
|
||||
Limit: tt.limit,
|
||||
Web: tt.web,
|
||||
Browser: br,
|
||||
CapiClient: func() (*capi.CAPIClient, error) {
|
||||
if tt.web {
|
||||
require.FailNow(t, "CapiClient was called with --web")
|
||||
}
|
||||
return capiClient, nil
|
||||
},
|
||||
}
|
||||
if tt.baseRepo != nil || tt.baseRepoErr != nil {
|
||||
baseRepo := tt.baseRepo
|
||||
|
|
@ -207,6 +251,10 @@ func Test_listRun(t *testing.T) {
|
|||
}
|
||||
got := stdout.String()
|
||||
require.Equal(t, tt.wantOut, got)
|
||||
require.Equal(t, tt.wantStderr, stderr.String())
|
||||
if tt.web {
|
||||
br.Verify(t, tt.wantBrowserURL)
|
||||
}
|
||||
reg.Verify(t)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue