Merge pull request #11211 from samcoe/fix-web-code-search-filename-extension-flags

Transform `extension` and `filename` qualifiers into `path` qualifier for web code search
This commit is contained in:
Kynan Ware 2025-07-17 12:09:58 -06:00 committed by GitHub
commit 5ea34d80de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 99 additions and 6 deletions

View file

@ -6,16 +6,19 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/gh"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/search/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/search"
ghauth "github.com/cli/go-gh/v2/pkg/auth"
"github.com/spf13/cobra"
)
type CodeOptions struct {
Browser browser.Browser
Config func() (gh.Config, error)
Exporter cmdutil.Exporter
IO *iostreams.IOStreams
Query search.Query
@ -26,6 +29,7 @@ type CodeOptions struct {
func NewCmdCode(f *cmdutil.Factory, runF func(*CodeOptions) error) *cobra.Command {
opts := &CodeOptions{
Browser: f.Browser,
Config: f.Config,
IO: f.IOStreams,
Query: search.Query{Kind: search.KindCode},
}
@ -104,8 +108,26 @@ func NewCmdCode(f *cmdutil.Factory, runF func(*CodeOptions) error) *cobra.Comman
func codeRun(opts *CodeOptions) error {
io := opts.IO
if opts.WebMode {
// FIXME: convert legacy `filename` and `extension` ES qualifiers to Blackbird's `path` qualifier
// when opening web search, otherwise the Blackbird search UI will complain.
// Convert `filename` and `extension` legacy search qualifiers to the new code search's `path`
// qualifier when used with `--web` because they are incompatible.
if opts.Query.Qualifiers.Filename != "" || opts.Query.Qualifiers.Extension != "" {
cfg, err := opts.Config()
if err != nil {
return err
}
host, _ := cfg.Authentication().DefaultHost()
// FIXME: Remove this check once GHES supports the new `path` search qualifier.
if !ghauth.IsEnterprise(host) {
filename := opts.Query.Qualifiers.Filename
extension := opts.Query.Qualifiers.Extension
if extension != "" && !strings.HasPrefix(extension, ".") {
extension = "." + extension
}
opts.Query.Qualifiers.Filename = ""
opts.Query.Qualifiers.Extension = ""
opts.Query.Qualifiers.Path = fmt.Sprintf("%s%s", filename, extension)
}
}
url := opts.Searcher.URL(opts.Query)
if io.IsStdoutTTY() {
fmt.Fprintf(io.ErrOut, "Opening %s in your browser.\n", text.DisplayURL(url))

View file

@ -6,6 +6,9 @@ import (
"testing"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/gh"
ghmock "github.com/cli/cli/v2/internal/gh/mock"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/search"
@ -146,6 +149,7 @@ func TestCodeRun(t *testing.T) {
wantErr bool
wantStderr string
wantStdout string
wantBrowse string
}{
{
name: "displays results tty",
@ -294,8 +298,7 @@ func TestCodeRun(t *testing.T) {
{
name: "opens browser for web mode tty",
opts: &CodeOptions{
Browser: &browser.Stub{},
Query: query,
Query: query,
Searcher: &search.SearcherMock{
URLFunc: func(query search.Query) string {
return "https://github.com/search?type=code&q=map+repo%3Acli%2Fcli"
@ -305,12 +308,12 @@ func TestCodeRun(t *testing.T) {
},
tty: true,
wantStderr: "Opening https://github.com/search in your browser.\n",
wantBrowse: "https://github.com/search?type=code&q=map+repo%3Acli%2Fcli",
},
{
name: "opens browser for web mode notty",
opts: &CodeOptions{
Browser: &browser.Stub{},
Query: query,
Query: query,
Searcher: &search.SearcherMock{
URLFunc: func(query search.Query) string {
return "https://github.com/search?type=code&q=map+repo%3Acli%2Fcli"
@ -318,6 +321,70 @@ func TestCodeRun(t *testing.T) {
},
WebMode: true,
},
wantBrowse: "https://github.com/search?type=code&q=map+repo%3Acli%2Fcli",
},
{
name: "converts filename and extension qualifiers for github.com web search",
opts: &CodeOptions{
Config: func() (gh.Config, error) { return config.NewBlankConfig(), nil },
Query: search.Query{
Keywords: []string{"map"},
Kind: "code",
Limit: 30,
Qualifiers: search.Qualifiers{
Filename: "testing",
Extension: "go",
},
},
Searcher: search.NewSearcher(nil, "github.com"),
WebMode: true,
},
wantBrowse: "https://github.com/search?q=map+path%3Atesting.go&type=code",
},
{
name: "properly handles extension with dot prefix when converting to path qualifier",
opts: &CodeOptions{
Config: func() (gh.Config, error) { return config.NewBlankConfig(), nil },
Query: search.Query{
Keywords: []string{"map"},
Kind: "code",
Limit: 30,
Qualifiers: search.Qualifiers{
Filename: "testing",
Extension: ".cpp",
},
},
Searcher: search.NewSearcher(nil, "github.com"),
WebMode: true,
},
wantBrowse: "https://github.com/search?q=map+path%3Atesting.cpp&type=code",
},
{
name: "does not convert filename and extension qualifiers for GHES web search",
opts: &CodeOptions{
Config: func() (gh.Config, error) {
cfg := &ghmock.ConfigMock{
AuthenticationFunc: func() gh.AuthConfig {
authCfg := &config.AuthConfig{}
authCfg.SetDefaultHost("example.com", "GH_HOST")
return authCfg
},
}
return cfg, nil
},
Query: search.Query{
Keywords: []string{"map"},
Kind: "code",
Limit: 30,
Qualifiers: search.Qualifiers{
Filename: "testing",
Extension: "go",
},
},
Searcher: search.NewSearcher(nil, "example.com"),
WebMode: true,
},
wantBrowse: "https://example.com/search?q=map+extension%3Ago+filename%3Atesting&type=code",
},
}
@ -327,6 +394,8 @@ func TestCodeRun(t *testing.T) {
ios.SetStdoutTTY(tt.tty)
ios.SetStderrTTY(tt.tty)
tt.opts.IO = ios
browser := &browser.Stub{}
tt.opts.Browser = browser
t.Run(tt.name, func(t *testing.T) {
err := codeRun(tt.opts)
if tt.wantErr {
@ -337,6 +406,7 @@ func TestCodeRun(t *testing.T) {
}
assert.Equal(t, tt.wantStdout, stdout.String())
assert.Equal(t, tt.wantStderr, stderr.String())
browser.Verify(t, tt.wantBrowse)
})
}
}

View file

@ -64,6 +64,7 @@ type Qualifiers struct {
Milestone string
No []string
Parent string
Path string
Project string
Pushed string
Reactions string