Transform extension and filename qualifiers into path qualifier

This commit is contained in:
Sam Coe 2025-07-01 14:10:30 +02:00
parent 4c47bad604
commit 545b420ff8
No known key found for this signature in database
3 changed files with 88 additions and 6 deletions

View file

@ -6,6 +6,8 @@ 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/ghinstance"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/search/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
@ -16,6 +18,7 @@ import (
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,33 @@ 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 legacy `filename` and `extension` elasticsearch qualifiers to Blackbird's `path`
// qualifier when opening web search.
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 Blackbird.
if host == ghinstance.Default() {
filename, extension, _ := strings.Cut(opts.Query.Qualifiers.Filename, ".")
if filename == "" {
filename = "*"
}
// Prioritize file extension that may have been provided with the `--filename` flag.
if extension == "" {
if opts.Query.Qualifiers.Extension != "" {
extension, _ = strings.CutPrefix(opts.Query.Qualifiers.Extension, ".")
} else {
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,52 @@ 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: "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 +376,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 +388,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