diff --git a/go.mod b/go.mod index bf7c8c646..43b2c73a3 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,9 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.5 github.com/MakeNowJust/heredoc v1.0.0 github.com/briandowns/spinner v1.18.1 - github.com/charmbracelet/glamour v0.4.0 + github.com/charmbracelet/glamour v0.5.0 github.com/charmbracelet/lipgloss v0.5.0 - github.com/cli/browser v1.1.0 - github.com/cli/go-gh v0.1.0 + github.com/cli/go-gh v0.1.1-0.20220817122932-3630ab390fe7 github.com/cli/oauth v0.9.0 github.com/cli/safeexec v1.0.0 github.com/cli/shurcooL-graphql v0.0.1 @@ -22,14 +21,13 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.3.0 github.com/henvic/httpretty v0.0.6 - github.com/itchyny/gojq v0.12.8 github.com/joho/godotenv v1.4.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.3.0 - github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 + github.com/muesli/termenv v0.12.0 github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38 github.com/opentracing/opentracing-go v1.1.0 github.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b @@ -48,6 +46,7 @@ require ( require ( github.com/alecthomas/chroma v0.10.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/cli/browser v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/fatih/color v1.7.0 // indirect @@ -55,10 +54,11 @@ require ( github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/itchyny/gojq v0.12.8 // indirect github.com/itchyny/timefmt-go v0.1.3 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/microcosm-cc/bluemonday v1.0.17 // indirect + github.com/microcosm-cc/bluemonday v1.0.19 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect diff --git a/go.sum b/go.sum index 7d9632582..08466abca 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3 github.com/briandowns/spinner v1.18.1 h1:yhQmQtM1zsqFsouh09Bk/jCjd50pC3EOGsh28gLVvwY= github.com/briandowns/spinner v1.18.1/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/charmbracelet/glamour v0.4.0 h1:scR+smyB7WdmrlIaff6IVlm48P48JaNM7JypM/VGl4k= -github.com/charmbracelet/glamour v0.4.0/go.mod h1:9ZRtG19AUIzcTm7FGLGbq3D5WKQ5UyZBbQsMQN0XIqc= +github.com/charmbracelet/glamour v0.5.0 h1:wu15ykPdB7X6chxugG/NNfDUbyyrCLV9XBalj5wdu3g= +github.com/charmbracelet/glamour v0.5.0/go.mod h1:9ZRtG19AUIzcTm7FGLGbq3D5WKQ5UyZBbQsMQN0XIqc= github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -58,8 +58,8 @@ github.com/cli/browser v1.1.0 h1:xOZBfkfY9L9vMBgqb1YwRirGu6QFaQ5dP/vXt5ENSOY= github.com/cli/browser v1.1.0/go.mod h1:HKMQAt9t12kov91Mn7RfZxyJQQgWgyS/3SZswlZ5iTI= github.com/cli/crypto v0.0.0-20210929142629-6be313f59b03 h1:3f4uHLfWx4/WlnMPXGai03eoWAI+oGHJwr+5OXfxCr8= github.com/cli/crypto v0.0.0-20210929142629-6be313f59b03/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -github.com/cli/go-gh v0.1.0 h1:kMqFmC3ECBrV2UKzlOHjNOTTchExVc5tjNHtCqk/zYk= -github.com/cli/go-gh v0.1.0/go.mod h1:eTGWl99EMZ+3Iau5C6dHyGAJRRia65MtdBtuhWc+84o= +github.com/cli/go-gh v0.1.1-0.20220817122932-3630ab390fe7 h1:yLnT5/sLlUcnr5njBRmf/d7pEKr0Cu2TpeecdlQl8AY= +github.com/cli/go-gh v0.1.1-0.20220817122932-3630ab390fe7/go.mod h1:UKRuMl3ZaitTvO4LPWj5bVw7QwZHnLu0S0lI9WWbdpc= github.com/cli/oauth v0.9.0 h1:nxBC0Df4tUzMkqffAB+uZvisOwT3/N9FpkfdTDtafxc= github.com/cli/oauth v0.9.0/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4= github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= @@ -199,14 +199,16 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/microcosm-cc/bluemonday v1.0.17 h1:Z1a//hgsQ4yjC+8zEkV8IWySkXnsxmdSY642CTFQb5Y= github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= +github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= +github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= -github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 h1:STjmj0uFfRryL9fzRA/OupNppeAID6QJYPMavTL7jtY= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= +github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38 h1:0FrBxrkJ0hVembTb/e4EU5Ml6vLcOusAqymmYISg5Uo= github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -368,6 +370,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= diff --git a/internal/authflow/flow.go b/internal/authflow/flow.go index 5a495b2a6..53b65af9e 100644 --- a/internal/authflow/flow.go +++ b/internal/authflow/flow.go @@ -11,8 +11,8 @@ import ( "strings" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghinstance" - "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/utils" "github.com/cli/oauth" @@ -106,8 +106,8 @@ func authFlow(oauthHost string, IO *iostreams.IOStreams, notice string, addition fmt.Fprintf(w, "%s to open %s in your browser... ", cs.Bold("Press Enter"), oauthHost) _ = waitForEnter(IO.In) - browser := cmdutil.NewBrowser(browserLauncher, IO.Out, IO.ErrOut) - if err := browser.Browse(authURL); err != nil { + b := browser.New(browserLauncher, IO.Out, IO.ErrOut) + if err := b.Browse(authURL); err != nil { fmt.Fprintf(w, "%s Failed opening a web browser at %s\n", cs.Red("!"), authURL) fmt.Fprintf(w, " %s\n", err) fmt.Fprint(w, " Please try entering the URL in your browser manually\n") diff --git a/internal/browser/browser.go b/internal/browser/browser.go new file mode 100644 index 000000000..e3c225b0f --- /dev/null +++ b/internal/browser/browser.go @@ -0,0 +1,16 @@ +package browser + +import ( + "io" + + ghBrowser "github.com/cli/go-gh/pkg/browser" +) + +type Browser interface { + Browse(string) error +} + +func New(launcher string, stdout, stderr io.Writer) Browser { + b := ghBrowser.New(launcher, stdout, stderr) + return &b +} diff --git a/internal/browser/stub.go b/internal/browser/stub.go new file mode 100644 index 000000000..52548affd --- /dev/null +++ b/internal/browser/stub.go @@ -0,0 +1,40 @@ +package browser + +type Stub struct { + urls []string +} + +func (b *Stub) Browse(url string) error { + b.urls = append(b.urls, url) + return nil +} + +func (b *Stub) BrowsedURL() string { + if len(b.urls) > 0 { + return b.urls[0] + } + return "" +} + +type _testing interface { + Errorf(string, ...interface{}) + Helper() +} + +func (b *Stub) Verify(t _testing, url string) { + t.Helper() + if url != "" { + switch len(b.urls) { + case 0: + t.Errorf("expected browser to open URL %q, but it was never invoked", url) + case 1: + if url != b.urls[0] { + t.Errorf("expected browser to open URL %q, got %q", url, b.urls[0]) + } + default: + t.Errorf("expected browser to open one URL, but was invoked %d times", len(b.urls)) + } + } else if len(b.urls) > 0 { + t.Errorf("expected no browser to open, but was invoked %d times: %v", len(b.urls), b.urls) + } +} diff --git a/pkg/cmd/api/api.go b/pkg/cmd/api/api.go index 73bb66157..3de9484aa 100644 --- a/pkg/cmd/api/api.go +++ b/pkg/cmd/api/api.go @@ -20,9 +20,10 @@ import ( "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/factory" "github.com/cli/cli/v2/pkg/cmdutil" - "github.com/cli/cli/v2/pkg/export" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/pkg/jsoncolor" + "github.com/cli/go-gh/pkg/jq" + "github.com/cli/go-gh/pkg/template" "github.com/spf13/cobra" ) @@ -297,7 +298,11 @@ func apiRun(opts *ApiOptions) error { host = opts.Hostname } - template := export.NewTemplate(opts.IO, opts.Template) + tmpl := template.New(bodyWriter, opts.IO.TerminalWidth(), opts.IO.ColorEnabled()) + err = tmpl.Parse(opts.Template) + if err != nil { + return err + } hasNextPage := true for hasNextPage { @@ -306,7 +311,7 @@ func apiRun(opts *ApiOptions) error { return err } - endCursor, err := processResponse(resp, opts, bodyWriter, headersWriter, &template) + endCursor, err := processResponse(resp, opts, bodyWriter, headersWriter, &tmpl) if err != nil { return err } @@ -330,10 +335,10 @@ func apiRun(opts *ApiOptions) error { } } - return template.End() + return tmpl.Flush() } -func processResponse(resp *http.Response, opts *ApiOptions, bodyWriter, headersWriter io.Writer, template *export.Template) (endCursor string, err error) { +func processResponse(resp *http.Response, opts *ApiOptions, bodyWriter, headersWriter io.Writer, template *template.Template) (endCursor string, err error) { if opts.ShowResponseHeaders { fmt.Fprintln(headersWriter, resp.Proto, resp.Status) printHeaders(headersWriter, resp.Header, opts.IO.ColorEnabled()) @@ -365,13 +370,12 @@ func processResponse(resp *http.Response, opts *ApiOptions, bodyWriter, headersW if opts.FilterOutput != "" && serverError == "" { // TODO: reuse parsed query across pagination invocations - err = export.FilterJSON(bodyWriter, responseBody, opts.FilterOutput) + err = jq.Evaluate(responseBody, bodyWriter, opts.FilterOutput) if err != nil { return } } else if opts.Template != "" && serverError == "" { - // TODO: reuse parsed template across pagination invocations - err = template.Execute(bodyWriter, responseBody) + err = template.Execute(responseBody) if err != nil { return } diff --git a/pkg/cmd/api/api_test.go b/pkg/cmd/api/api_test.go index 0e25cd7f4..9056e1cf7 100644 --- a/pkg/cmd/api/api_test.go +++ b/pkg/cmd/api/api_test.go @@ -17,8 +17,8 @@ import ( "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmdutil" - "github.com/cli/cli/v2/pkg/export" "github.com/cli/cli/v2/pkg/iostreams" + "github.com/cli/go-gh/pkg/template" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1298,11 +1298,13 @@ func Test_processResponse_template(t *testing.T) { IO: ios, Template: `{{range .}}{{.title}} ({{.labels | pluck "name" | join ", " }}){{"\n"}}{{end}}`, } - template := export.NewTemplate(ios, opts.Template) - _, err := processResponse(&resp, &opts, ios.Out, io.Discard, &template) - require.NoError(t, err) - err = template.End() + tmpl := template.New(ios.Out, ios.TerminalWidth(), ios.ColorEnabled()) + err := tmpl.Parse(opts.Template) + require.NoError(t, err) + _, err = processResponse(&resp, &opts, ios.Out, io.Discard, &tmpl) + require.NoError(t, err) + err = tmpl.Flush() require.NoError(t, err) assert.Equal(t, heredoc.Doc(` diff --git a/pkg/cmd/browse/browse.go b/pkg/cmd/browse/browse.go index 5129e45a9..19d7b5e6e 100644 --- a/pkg/cmd/browse/browse.go +++ b/pkg/cmd/browse/browse.go @@ -13,6 +13,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/git" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" @@ -20,13 +21,9 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type BrowseOptions struct { BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser HttpClient func() (*http.Client, error) IO *iostreams.IOStreams PathFromRepoRoot func() string diff --git a/pkg/cmd/browse/browse_test.go b/pkg/cmd/browse/browse_test.go index 380a6fc1c..aa9034b78 100644 --- a/pkg/cmd/browse/browse_test.go +++ b/pkg/cmd/browse/browse_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/cli/cli/v2/git" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/httpmock" @@ -448,7 +449,7 @@ func Test_runBrowse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ios, _, stdout, stderr := iostreams.Test() - browser := cmdutil.TestBrowser{} + browser := browser.Stub{} reg := httpmock.Registry{} defer reg.Verify(t) diff --git a/pkg/cmd/codespace/code_test.go b/pkg/cmd/codespace/code_test.go index e7330c072..8c79b1d99 100644 --- a/pkg/cmd/codespace/code_test.go +++ b/pkg/cmd/codespace/code_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/codespaces/api" - "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" ) @@ -62,7 +62,7 @@ func TestApp_VSCode(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - b := &cmdutil.TestBrowser{} + b := &browser.Stub{} ios, _, stdout, stderr := iostreams.Test() a := &App{ browser: b, diff --git a/pkg/cmd/codespace/common.go b/pkg/cmd/codespace/common.go index 759cec3ce..1ee3b33ae 100644 --- a/pkg/cmd/codespace/common.go +++ b/pkg/cmd/codespace/common.go @@ -13,6 +13,7 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2/terminal" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/codespaces" "github.com/cli/cli/v2/internal/codespaces/api" "github.com/cli/cli/v2/pkg/iostreams" @@ -22,10 +23,6 @@ import ( "golang.org/x/term" ) -type browser interface { - Browse(string) error -} - type executable interface { Executable() string } @@ -35,10 +32,10 @@ type App struct { apiClient apiClient errLogger *log.Logger executable executable - browser browser + browser browser.Browser } -func NewApp(io *iostreams.IOStreams, exe executable, apiClient apiClient, browser browser) *App { +func NewApp(io *iostreams.IOStreams, exe executable, apiClient apiClient, browser browser.Browser) *App { errLogger := log.New(io.ErrOut, "", 0) return &App{ diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index e348bf5e0..a53ee527a 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -10,6 +10,7 @@ import ( "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/context" "github.com/cli/cli/v2/git" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/prompter" @@ -33,7 +34,7 @@ func New(appVersion string) *cmdutil.Factory { f.Remotes = remotesFunc(f) // Depends on Config f.BaseRepo = BaseRepoFunc(f) // Depends on Remotes f.Prompter = newPrompter(f) // Depends on Config and IOStreams - f.Browser = browser(f) // Depends on Config, and IOStreams + f.Browser = newBrowser(f) // Depends on Config, and IOStreams f.ExtensionManager = extensionManager(f) // Depends on Config, HttpClient, and IOStreams return f @@ -105,9 +106,9 @@ func httpClientFunc(f *cmdutil.Factory, appVersion string) func() (*http.Client, } } -func browser(f *cmdutil.Factory) cmdutil.Browser { +func newBrowser(f *cmdutil.Factory) browser.Browser { io := f.IOStreams - return cmdutil.NewBrowser(browserLauncher(f), io.Out, io.ErrOut) + return browser.New(browserLauncher(f), io.Out, io.ErrOut) } func newPrompter(f *cmdutil.Factory) prompter.Prompter { diff --git a/pkg/cmd/gist/create/create.go b/pkg/cmd/gist/create/create.go index f61da9d70..1055da468 100644 --- a/pkg/cmd/gist/create/create.go +++ b/pkg/cmd/gist/create/create.go @@ -15,6 +15,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghinstance" "github.com/cli/cli/v2/pkg/cmd/gist/shared" @@ -24,10 +25,6 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type CreateOptions struct { IO *iostreams.IOStreams @@ -39,7 +36,7 @@ type CreateOptions struct { Config func() (config.Config, error) HttpClient func() (*http.Client, error) - Browser browser + Browser browser.Browser } func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command { diff --git a/pkg/cmd/gist/create/create_test.go b/pkg/cmd/gist/create/create_test.go index 4c5ee43e6..3fd60fed9 100644 --- a/pkg/cmd/gist/create/create_test.go +++ b/pkg/cmd/gist/create/create_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/run" "github.com/cli/cli/v2/pkg/cmd/gist/shared" @@ -337,7 +338,7 @@ func Test_createRun(t *testing.T) { ios, stdin, stdout, stderr := iostreams.Test() tt.opts.IO = ios - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} tt.opts.Browser = browser _, teardown := run.Stub() diff --git a/pkg/cmd/issue/comment/comment_test.go b/pkg/cmd/issue/comment/comment_test.go index 80935066d..edf1900d3 100644 --- a/pkg/cmd/issue/comment/comment_test.go +++ b/pkg/cmd/issue/comment/comment_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "testing" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" @@ -152,7 +153,7 @@ func TestNewCmdComment(t *testing.T) { f := &cmdutil.Factory{ IOStreams: ios, - Browser: &cmdutil.TestBrowser{}, + Browser: &browser.Stub{}, } argv, err := shlex.Split(tt.input) diff --git a/pkg/cmd/issue/create/create.go b/pkg/cmd/issue/create/create.go index 64d1b3cf2..f12b6006f 100644 --- a/pkg/cmd/issue/create/create.go +++ b/pkg/cmd/issue/create/create.go @@ -6,6 +6,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/pr/shared" @@ -16,16 +17,12 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type CreateOptions struct { HttpClient func() (*http.Client, error) Config func() (config.Config, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser RootDirOverride string diff --git a/pkg/cmd/issue/create/create_test.go b/pkg/cmd/issue/create/create_test.go index 6d1928f0d..9fc9c30ea 100644 --- a/pkg/cmd/issue/create/create_test.go +++ b/pkg/cmd/issue/create/create_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" @@ -267,7 +268,7 @@ func Test_createRun(t *testing.T) { opts.BaseRepo = func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil } - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} opts.Browser = browser err := createRun(opts) @@ -297,7 +298,7 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli strin ios.SetStdinTTY(isTTY) ios.SetStderrTTY(isTTY) - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} factory := &cmdutil.Factory{ IOStreams: ios, HttpClient: func() (*http.Client, error) { diff --git a/pkg/cmd/issue/list/list.go b/pkg/cmd/issue/list/list.go index 3bc076644..efd054974 100644 --- a/pkg/cmd/issue/list/list.go +++ b/pkg/cmd/issue/list/list.go @@ -9,6 +9,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghinstance" "github.com/cli/cli/v2/internal/ghrepo" @@ -23,16 +24,12 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type ListOptions struct { HttpClient func() (*http.Client, error) Config func() (config.Config, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser WebMode bool Exporter cmdutil.Exporter diff --git a/pkg/cmd/issue/list/list_test.go b/pkg/cmd/issue/list/list_test.go index e7d9760ec..645c83f01 100644 --- a/pkg/cmd/issue/list/list_test.go +++ b/pkg/cmd/issue/list/list_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" @@ -185,7 +186,7 @@ func TestIssueList_web(t *testing.T) { ios, _, stdout, stderr := iostreams.Test() ios.SetStdoutTTY(true) ios.SetStderrTTY(true) - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} reg := &httpmock.Registry{} defer reg.Verify(t) diff --git a/pkg/cmd/issue/view/view.go b/pkg/cmd/issue/view/view.go index 2b5983259..28dc3a6d7 100644 --- a/pkg/cmd/issue/view/view.go +++ b/pkg/cmd/issue/view/view.go @@ -10,6 +10,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" issueShared "github.com/cli/cli/v2/pkg/cmd/issue/shared" prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared" @@ -21,15 +22,11 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser SelectorArg string WebMode bool diff --git a/pkg/cmd/issue/view/view_test.go b/pkg/cmd/issue/view/view_test.go index f0b8417b8..ff26f4bc3 100644 --- a/pkg/cmd/issue/view/view_test.go +++ b/pkg/cmd/issue/view/view_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" @@ -61,7 +62,7 @@ func TestIssueView_web(t *testing.T) { ios, _, stdout, stderr := iostreams.Test() ios.SetStdoutTTY(true) ios.SetStderrTTY(true) - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} reg := &httpmock.Registry{} defer reg.Verify(t) diff --git a/pkg/cmd/label/list.go b/pkg/cmd/label/list.go index 75351e59c..98256c048 100644 --- a/pkg/cmd/label/list.go +++ b/pkg/cmd/label/list.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" @@ -13,13 +14,9 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type listOptions struct { BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser HttpClient func() (*http.Client, error) IO *iostreams.IOStreams diff --git a/pkg/cmd/label/list_test.go b/pkg/cmd/label/list_test.go index 36be2181a..7e7a9a93d 100644 --- a/pkg/cmd/label/list_test.go +++ b/pkg/cmd/label/list_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/httpmock" @@ -362,7 +363,7 @@ func TestListRun(t *testing.T) { ios.SetStdinTTY(tt.tty) ios.SetStderrTTY(tt.tty) tt.opts.IO = ios - tt.opts.Browser = &cmdutil.TestBrowser{} + tt.opts.Browser = &browser.Stub{} tt.opts.BaseRepo = func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil } diff --git a/pkg/cmd/pr/checks/checks.go b/pkg/cmd/pr/checks/checks.go index 3f4cc1d86..65e2aa9e1 100644 --- a/pkg/cmd/pr/checks/checks.go +++ b/pkg/cmd/pr/checks/checks.go @@ -6,6 +6,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" @@ -16,13 +17,9 @@ import ( const defaultInterval time.Duration = 10 * time.Second -type browser interface { - Browse(string) error -} - type ChecksOptions struct { IO *iostreams.IOStreams - Browser browser + Browser browser.Browser Finder shared.PRFinder diff --git a/pkg/cmd/pr/checks/checks_test.go b/pkg/cmd/pr/checks/checks_test.go index 7fbe0d96a..b0802f3db 100644 --- a/pkg/cmd/pr/checks/checks_test.go +++ b/pkg/cmd/pr/checks/checks_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" "github.com/cli/cli/v2/pkg/cmd/pr/shared" @@ -260,7 +261,7 @@ func TestChecksRun_web(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} ios, _, stdout, stderr := iostreams.Test() ios.SetStdoutTTY(tc.isTTY) diff --git a/pkg/cmd/pr/comment/comment_test.go b/pkg/cmd/pr/comment/comment_test.go index 973b211df..a5c0edfb1 100644 --- a/pkg/cmd/pr/comment/comment_test.go +++ b/pkg/cmd/pr/comment/comment_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" @@ -173,7 +174,7 @@ func TestNewCmdComment(t *testing.T) { f := &cmdutil.Factory{ IOStreams: ios, - Browser: &cmdutil.TestBrowser{}, + Browser: &browser.Stub{}, } argv, err := shlex.Split(tt.input) diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go index 31ff9c25c..d2149d5d8 100644 --- a/pkg/cmd/pr/create/create.go +++ b/pkg/cmd/pr/create/create.go @@ -14,6 +14,7 @@ import ( "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/context" "github.com/cli/cli/v2/git" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/pr/shared" @@ -24,10 +25,6 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type CreateOptions struct { // This struct stores user input and factory functions HttpClient func() (*http.Client, error) @@ -35,7 +32,7 @@ type CreateOptions struct { IO *iostreams.IOStreams Remotes func() (context.Remotes, error) Branch func() (string, error) - Browser browser + Browser browser.Browser Finder shared.PRFinder TitleProvided bool diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go index 47bea2463..c83dd13a2 100644 --- a/pkg/cmd/pr/create/create_test.go +++ b/pkg/cmd/pr/create/create_test.go @@ -12,6 +12,7 @@ import ( "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/context" "github.com/cli/cli/v2/git" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" @@ -844,7 +845,7 @@ func Test_createRun(t *testing.T) { ios.SetStdoutTTY(tt.tty) ios.SetStdinTTY(tt.tty) ios.SetStderrTTY(tt.tty) - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} opts.IO = ios opts.Browser = browser opts.HttpClient = func() (*http.Client, error) { diff --git a/pkg/cmd/pr/list/list.go b/pkg/cmd/pr/list/list.go index b33fe9ff5..bc20d1ee4 100644 --- a/pkg/cmd/pr/list/list.go +++ b/pkg/cmd/pr/list/list.go @@ -8,6 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" @@ -17,15 +18,11 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type ListOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser WebMode bool LimitResults int diff --git a/pkg/cmd/pr/list/list_test.go b/pkg/cmd/pr/list/list_test.go index 006f2e78b..44f26c05d 100644 --- a/pkg/cmd/pr/list/list_test.go +++ b/pkg/cmd/pr/list/list_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" "github.com/cli/cli/v2/pkg/cmdutil" @@ -24,7 +25,7 @@ func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, err ios.SetStdinTTY(isTTY) ios.SetStderrTTY(isTTY) - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} factory := &cmdutil.Factory{ IOStreams: ios, Browser: browser, diff --git a/pkg/cmd/pr/view/view.go b/pkg/cmd/pr/view/view.go index c385e1654..20947a01d 100644 --- a/pkg/cmd/pr/view/view.go +++ b/pkg/cmd/pr/view/view.go @@ -8,6 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" @@ -17,13 +18,9 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type ViewOptions struct { IO *iostreams.IOStreams - Browser browser + Browser browser.Browser Finder shared.PRFinder Exporter cmdutil.Exporter diff --git a/pkg/cmd/pr/view/view_test.go b/pkg/cmd/pr/view/view_test.go index a909f0b6c..41e28f257 100644 --- a/pkg/cmd/pr/view/view_test.go +++ b/pkg/cmd/pr/view/view_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" "github.com/cli/cli/v2/pkg/cmd/pr/shared" @@ -118,7 +119,7 @@ func runCommand(rt http.RoundTripper, branch string, isTTY bool, cli string) (*t ios.SetStdinTTY(isTTY) ios.SetStderrTTY(isTTY) - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} factory := &cmdutil.Factory{ IOStreams: ios, Browser: browser, diff --git a/pkg/cmd/repo/view/view.go b/pkg/cmd/repo/view/view.go index 25eebb0bb..bb3febaae 100644 --- a/pkg/cmd/repo/view/view.go +++ b/pkg/cmd/repo/view/view.go @@ -10,6 +10,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmdutil" @@ -19,15 +20,11 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser Exporter cmdutil.Exporter Config func() (config.Config, error) diff --git a/pkg/cmd/repo/view/view_test.go b/pkg/cmd/repo/view/view_test.go index cb4bc24fa..16b525783 100644 --- a/pkg/cmd/repo/view/view_test.go +++ b/pkg/cmd/repo/view/view_test.go @@ -8,6 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" @@ -121,7 +122,7 @@ func Test_RepoView_Web(t *testing.T) { reg := &httpmock.Registry{} reg.StubRepoInfoResponse("OWNER", "REPO", "main") - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} opts := &ViewOptions{ Web: true, HttpClient: func() (*http.Client, error) { diff --git a/pkg/cmd/run/view/view.go b/pkg/cmd/run/view/view.go index 973a67897..5f185ee0d 100644 --- a/pkg/cmd/run/view/view.go +++ b/pkg/cmd/run/view/view.go @@ -17,6 +17,7 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghinstance" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/run/shared" @@ -27,10 +28,6 @@ import ( "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - type runLogCache interface { Exists(string) bool Create(string, io.ReadCloser) error @@ -67,7 +64,7 @@ type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - Browser browser + Browser browser.Browser RunLogCache runLogCache RunID string diff --git a/pkg/cmd/run/view/view_test.go b/pkg/cmd/run/view/view_test.go index c2a9e51c9..b544bac97 100644 --- a/pkg/cmd/run/view/view_test.go +++ b/pkg/cmd/run/view/view_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/run/shared" "github.com/cli/cli/v2/pkg/cmdutil" @@ -849,7 +850,7 @@ func TestViewRun(t *testing.T) { tt.askStubs(as) } - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} tt.opts.Browser = browser rlc := testRunLogCache{} tt.opts.RunLogCache = rlc diff --git a/pkg/cmd/search/repos/repos.go b/pkg/cmd/search/repos/repos.go index 57f376672..c0f528029 100644 --- a/pkg/cmd/search/repos/repos.go +++ b/pkg/cmd/search/repos/repos.go @@ -6,6 +6,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/pkg/cmd/search/shared" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" @@ -16,7 +17,7 @@ import ( ) type ReposOptions struct { - Browser cmdutil.Browser + Browser browser.Browser Exporter cmdutil.Exporter IO *iostreams.IOStreams Query search.Query diff --git a/pkg/cmd/search/repos/repos_test.go b/pkg/cmd/search/repos/repos_test.go index fc7bf6d49..010ee84cc 100644 --- a/pkg/cmd/search/repos/repos_test.go +++ b/pkg/cmd/search/repos/repos_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/pkg/search" @@ -237,7 +238,7 @@ func TestReposRun(t *testing.T) { { name: "opens browser for web mode tty", opts: &ReposOptions{ - Browser: &cmdutil.TestBrowser{}, + Browser: &browser.Stub{}, Query: query, Searcher: &search.SearcherMock{ URLFunc: func(query search.Query) string { @@ -252,7 +253,7 @@ func TestReposRun(t *testing.T) { { name: "opens browser for web mode notty", opts: &ReposOptions{ - Browser: &cmdutil.TestBrowser{}, + Browser: &browser.Stub{}, Query: query, Searcher: &search.SearcherMock{ URLFunc: func(query search.Query) string { diff --git a/pkg/cmd/search/shared/shared.go b/pkg/cmd/search/shared/shared.go index 918d95882..f5fea736e 100644 --- a/pkg/cmd/search/shared/shared.go +++ b/pkg/cmd/search/shared/shared.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/pkg/search" @@ -26,7 +27,7 @@ const ( ) type IssuesOptions struct { - Browser cmdutil.Browser + Browser browser.Browser Entity EntityType Exporter cmdutil.Exporter IO *iostreams.IOStreams diff --git a/pkg/cmd/search/shared/shared_test.go b/pkg/cmd/search/shared/shared_test.go index 1c4e621a9..66c9c9cc2 100644 --- a/pkg/cmd/search/shared/shared_test.go +++ b/pkg/cmd/search/shared/shared_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/pkg/cmd/factory" - "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/pkg/search" "github.com/stretchr/testify/assert" @@ -159,7 +159,7 @@ func TestSearchIssues(t *testing.T) { { name: "opens browser for web mode tty", opts: &IssuesOptions{ - Browser: &cmdutil.TestBrowser{}, + Browser: &browser.Stub{}, Entity: Issues, Query: query, Searcher: &search.SearcherMock{ @@ -175,7 +175,7 @@ func TestSearchIssues(t *testing.T) { { name: "opens browser for web mode notty", opts: &IssuesOptions{ - Browser: &cmdutil.TestBrowser{}, + Browser: &browser.Stub{}, Entity: Issues, Query: query, Searcher: &search.SearcherMock{ diff --git a/pkg/cmd/workflow/view/view.go b/pkg/cmd/workflow/view/view.go index 58d0fe7b6..402be5101 100644 --- a/pkg/cmd/workflow/view/view.go +++ b/pkg/cmd/workflow/view/view.go @@ -9,6 +9,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" runShared "github.com/cli/cli/v2/pkg/cmd/run/shared" "github.com/cli/cli/v2/pkg/cmd/workflow/shared" @@ -23,7 +24,7 @@ type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) - Browser cmdutil.Browser + Browser browser.Browser Selector string Ref string diff --git a/pkg/cmd/workflow/view/view_test.go b/pkg/cmd/workflow/view/view_test.go index 315491111..412f9e755 100644 --- a/pkg/cmd/workflow/view/view_test.go +++ b/pkg/cmd/workflow/view/view_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/ghrepo" runShared "github.com/cli/cli/v2/pkg/cmd/run/shared" "github.com/cli/cli/v2/pkg/cmd/workflow/shared" @@ -412,7 +413,7 @@ func TestViewRun(t *testing.T) { return ghrepo.FromFullName("OWNER/REPO") } - browser := &cmdutil.TestBrowser{} + browser := &browser.Stub{} tt.opts.Browser = browser t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/cmdutil/factory.go b/pkg/cmdutil/factory.go index 3f3f0de0e..d7357f1fa 100644 --- a/pkg/cmdutil/factory.go +++ b/pkg/cmdutil/factory.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/cli/cli/v2/context" + "github.com/cli/cli/v2/internal/browser" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/prompter" @@ -14,14 +15,10 @@ import ( "github.com/cli/cli/v2/pkg/iostreams" ) -type Browser interface { - Browse(string) error -} - type Factory struct { IOStreams *iostreams.IOStreams - Browser Browser Prompter prompter.Prompter + Browser browser.Browser HttpClient func() (*http.Client, error) BaseRepo func() (ghrepo.Interface, error) diff --git a/pkg/cmdutil/json_flags.go b/pkg/cmdutil/json_flags.go index e44c33405..2d327c337 100644 --- a/pkg/cmdutil/json_flags.go +++ b/pkg/cmdutil/json_flags.go @@ -10,10 +10,11 @@ import ( "sort" "strings" - "github.com/cli/cli/v2/pkg/export" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/pkg/jsoncolor" "github.com/cli/cli/v2/pkg/set" + "github.com/cli/go-gh/pkg/jq" + "github.com/cli/go-gh/pkg/template" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -137,9 +138,16 @@ func (e *exportFormat) Write(ios *iostreams.IOStreams, data interface{}) error { w := ios.Out if e.filter != "" { - return export.FilterJSON(w, &buf, e.filter) + return jq.Evaluate(&buf, w, e.filter) } else if e.template != "" { - return export.ExecuteTemplate(ios, &buf, e.template) + t := template.New(w, ios.TerminalWidth(), ios.ColorEnabled()) + if err := t.Parse(e.template); err != nil { + return err + } + if err := t.Execute(&buf); err != nil { + return err + } + return t.Flush() } else if ios.ColorEnabled() { return jsoncolor.Write(w, &buf, " ") } diff --git a/pkg/cmdutil/web_browser.go b/pkg/cmdutil/web_browser.go deleted file mode 100644 index aa33373cb..000000000 --- a/pkg/cmdutil/web_browser.go +++ /dev/null @@ -1,83 +0,0 @@ -package cmdutil - -import ( - "io" - "os/exec" - - "github.com/cli/browser" - "github.com/cli/safeexec" - "github.com/google/shlex" -) - -func NewBrowser(launcher string, stdout, stderr io.Writer) Browser { - return &webBrowser{ - launcher: launcher, - stdout: stdout, - stderr: stderr, - } -} - -type webBrowser struct { - launcher string - stdout io.Writer - stderr io.Writer -} - -func (b *webBrowser) Browse(url string) error { - if b.launcher != "" { - launcherArgs, err := shlex.Split(b.launcher) - if err != nil { - return err - } - launcherExe, err := safeexec.LookPath(launcherArgs[0]) - if err != nil { - return err - } - args := append(launcherArgs[1:], url) - cmd := exec.Command(launcherExe, args...) - cmd.Stdout = b.stdout - cmd.Stderr = b.stderr - return cmd.Run() - } - - return browser.OpenURL(url) -} - -type TestBrowser struct { - urls []string -} - -func (b *TestBrowser) Browse(url string) error { - b.urls = append(b.urls, url) - return nil -} - -func (b *TestBrowser) BrowsedURL() string { - if len(b.urls) > 0 { - return b.urls[0] - } - return "" -} - -type _testing interface { - Errorf(string, ...interface{}) - Helper() -} - -func (b *TestBrowser) Verify(t _testing, url string) { - t.Helper() - if url != "" { - switch len(b.urls) { - case 0: - t.Errorf("expected browser to open URL %q, but it was never invoked", url) - case 1: - if url != b.urls[0] { - t.Errorf("expected browser to open URL %q, got %q", url, b.urls[0]) - } - default: - t.Errorf("expected browser to open one URL, but was invoked %d times", len(b.urls)) - } - } else if len(b.urls) > 0 { - t.Errorf("expected no browser to open, but was invoked %d times: %v", len(b.urls), b.urls) - } -} diff --git a/pkg/export/filter.go b/pkg/export/filter.go deleted file mode 100644 index 8480e22f8..000000000 --- a/pkg/export/filter.go +++ /dev/null @@ -1,71 +0,0 @@ -package export - -import ( - "encoding/json" - "fmt" - "io" - "os" - - "github.com/itchyny/gojq" -) - -func FilterJSON(w io.Writer, input io.Reader, queryStr string) error { - query, err := gojq.Parse(queryStr) - if err != nil { - return err - } - - code, err := gojq.Compile( - query, - gojq.WithEnvironLoader(func() []string { - return os.Environ() - })) - - if err != nil { - return err - } - - jsonData, err := io.ReadAll(input) - if err != nil { - return err - } - - var responseData interface{} - err = json.Unmarshal(jsonData, &responseData) - if err != nil { - return err - } - - iter := code.Run(responseData) - for { - v, ok := iter.Next() - if !ok { - break - } - if err, isErr := v.(error); isErr { - return err - } - if text, e := jsonScalarToString(v); e == nil { - _, err := fmt.Fprintln(w, text) - if err != nil { - return err - } - } else { - var jsonFragment []byte - jsonFragment, err = json.Marshal(v) - if err != nil { - return err - } - _, err = w.Write(jsonFragment) - if err != nil { - return err - } - _, err = fmt.Fprint(w, "\n") - if err != nil { - return err - } - } - } - - return nil -} diff --git a/pkg/export/filter_test.go b/pkg/export/filter_test.go deleted file mode 100644 index 2c449f750..000000000 --- a/pkg/export/filter_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package export - -import ( - "bytes" - "io" - "strings" - "testing" - - "github.com/MakeNowJust/heredoc" -) - -func Test_filterJSON(t *testing.T) { - t.Setenv("CODE", "code_c") - - type args struct { - json io.Reader - query string - } - tests := []struct { - name string - args args - wantW string - wantErr bool - }{ - { - name: "simple", - args: args{ - json: strings.NewReader(`{"name":"Mona", "arms":8}`), - query: `.name`, - }, - wantW: "Mona\n", - }, - { - name: "multiple queries", - args: args{ - json: strings.NewReader(`{"name":"Mona", "arms":8}`), - query: `.name,.arms`, - }, - wantW: "Mona\n8\n", - }, - { - name: "object as JSON", - args: args{ - json: strings.NewReader(`{"user":{"login":"monalisa"}}`), - query: `.user`, - }, - wantW: "{\"login\":\"monalisa\"}\n", - }, - { - name: "complex", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - { - "title": "First title", - "labels": [{"name":"bug"}, {"name":"help wanted"}] - }, - { - "title": "Second but not last", - "labels": [] - }, - { - "title": "Alas, tis' the end", - "labels": [{}, {"name":"feature"}] - } - ]`)), - query: `.[] | [.title,(.labels | map(.name) | join(","))] | @tsv`, - }, - wantW: heredoc.Doc(` - First title bug,help wanted - Second but not last - Alas, tis' the end ,feature - `), - }, - { - name: "with env var", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - { - "title": "code_a", - "labels": [{"name":"bug"}, {"name":"help wanted"}] - }, - { - "title": "code_b", - "labels": [] - }, - { - "title": "code_c", - "labels": [{}, {"name":"feature"}] - } - ]`)), - query: `.[]| select(.title == env.CODE) | .labels`, - }, - wantW: "[{},{\"name\":\"feature\"}]\n", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &bytes.Buffer{} - if err := FilterJSON(w, tt.args.json, tt.args.query); (err != nil) != tt.wantErr { - t.Errorf("filterJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if gotW := w.String(); gotW != tt.wantW { - t.Errorf("filterJSON() = %q, want %q", gotW, tt.wantW) - } - }) - } -} diff --git a/pkg/export/template.go b/pkg/export/template.go deleted file mode 100644 index 83630e1ae..000000000 --- a/pkg/export/template.go +++ /dev/null @@ -1,209 +0,0 @@ -package export - -import ( - "encoding/json" - "fmt" - "io" - "math" - "strconv" - "strings" - "text/template" - "time" - - "github.com/cli/cli/v2/pkg/iostreams" - "github.com/cli/cli/v2/pkg/text" - "github.com/cli/cli/v2/utils" - "github.com/mgutz/ansi" -) - -type Template struct { - io *iostreams.IOStreams - tablePrinter utils.TablePrinter - template *template.Template - templateStr string -} - -func NewTemplate(io *iostreams.IOStreams, template string) Template { - return Template{ - io: io, - templateStr: template, - } -} - -func (t *Template) parseTemplate(tpl string) (*template.Template, error) { - now := time.Now() - - templateFuncs := map[string]interface{}{ - "color": t.color, - "autocolor": t.color, - - "timefmt": func(format, input string) (string, error) { - t, err := time.Parse(time.RFC3339, input) - if err != nil { - return "", err - } - return t.Format(format), nil - }, - "timeago": func(input string) (string, error) { - t, err := time.Parse(time.RFC3339, input) - if err != nil { - return "", err - } - return timeAgo(now.Sub(t)), nil - }, - - "pluck": templatePluck, - "join": templateJoin, - "tablerow": t.tableRow, - "tablerender": t.tableRender, - "truncate": func(maxWidth int, v interface{}) (string, error) { - if v == nil { - return "", nil - } - if s, ok := v.(string); ok { - return text.Truncate(maxWidth, s), nil - } - return "", fmt.Errorf("invalid value; expected string, got %T", v) - }, - } - - if !t.io.ColorEnabled() { - templateFuncs["autocolor"] = func(colorName string, input interface{}) (string, error) { - return jsonScalarToString(input) - } - } - - return template.New("").Funcs(templateFuncs).Parse(tpl) -} - -func (t *Template) Execute(w io.Writer, input io.Reader) error { - if t.template == nil { - template, err := t.parseTemplate(t.templateStr) - if err != nil { - return err - } - - t.template = template - } - - jsonData, err := io.ReadAll(input) - if err != nil { - return err - } - - var data interface{} - if err := json.Unmarshal(jsonData, &data); err != nil { - return err - } - - return t.template.Execute(w, data) -} - -func ExecuteTemplate(io *iostreams.IOStreams, input io.Reader, template string) error { - t := NewTemplate(io, template) - if err := t.Execute(io.Out, input); err != nil { - return err - } - return t.End() -} - -func jsonScalarToString(input interface{}) (string, error) { - switch tt := input.(type) { - case string: - return tt, nil - case float64: - if math.Trunc(tt) == tt { - return strconv.FormatFloat(tt, 'f', 0, 64), nil - } else { - return strconv.FormatFloat(tt, 'f', 2, 64), nil - } - case nil: - return "", nil - case bool: - return fmt.Sprintf("%v", tt), nil - default: - return "", fmt.Errorf("cannot convert type to string: %v", tt) - } -} - -func (t *Template) color(colorName string, input interface{}) (string, error) { - text, err := jsonScalarToString(input) - if err != nil { - return "", err - } - return ansi.Color(text, colorName), nil -} - -func templatePluck(field string, input []interface{}) []interface{} { - var results []interface{} - for _, item := range input { - obj := item.(map[string]interface{}) - results = append(results, obj[field]) - } - return results -} - -func templateJoin(sep string, input []interface{}) (string, error) { - var results []string - for _, item := range input { - text, err := jsonScalarToString(item) - if err != nil { - return "", err - } - results = append(results, text) - } - return strings.Join(results, sep), nil -} - -func (t *Template) tableRow(fields ...interface{}) (string, error) { - if t.tablePrinter == nil { - t.tablePrinter = utils.NewTablePrinterWithOptions(t.io, utils.TablePrinterOptions{IsTTY: true}) - } - for _, e := range fields { - s, err := jsonScalarToString(e) - if err != nil { - return "", fmt.Errorf("failed to write table row: %v", err) - } - t.tablePrinter.AddField(s, text.TruncateColumn, nil) - } - t.tablePrinter.EndRow() - return "", nil -} - -func (t *Template) tableRender() (string, error) { - if t.tablePrinter != nil { - err := t.tablePrinter.Render() - t.tablePrinter = nil - if err != nil { - return "", fmt.Errorf("failed to render table: %v", err) - } - } - return "", nil -} - -func (t *Template) End() error { - // Finalize any template actions. - if _, err := t.tableRender(); err != nil { - return err - } - return nil -} - -func timeAgo(ago time.Duration) string { - if ago < time.Minute { - return "just now" - } - if ago < time.Hour { - return utils.Pluralize(int(ago.Minutes()), "minute") + " ago" - } - if ago < 24*time.Hour { - return utils.Pluralize(int(ago.Hours()), "hour") + " ago" - } - if ago < 30*24*time.Hour { - return utils.Pluralize(int(ago.Hours())/24, "day") + " ago" - } - if ago < 365*24*time.Hour { - return utils.Pluralize(int(ago.Hours())/24/30, "month") + " ago" - } - return utils.Pluralize(int(ago.Hours()/24/365), "year") + " ago" -} diff --git a/pkg/export/template_test.go b/pkg/export/template_test.go deleted file mode 100644 index 5167f7cc9..000000000 --- a/pkg/export/template_test.go +++ /dev/null @@ -1,312 +0,0 @@ -package export - -import ( - "fmt" - "io" - "strings" - "testing" - "time" - - "github.com/MakeNowJust/heredoc" - "github.com/cli/cli/v2/pkg/iostreams" -) - -func Test_jsonScalarToString(t *testing.T) { - tests := []struct { - name string - input interface{} - want string - wantErr bool - }{ - { - name: "string", - input: "hello", - want: "hello", - }, - { - name: "int", - input: float64(1234), - want: "1234", - }, - { - name: "float", - input: float64(12.34), - want: "12.34", - }, - { - name: "null", - input: nil, - want: "", - }, - { - name: "true", - input: true, - want: "true", - }, - { - name: "false", - input: false, - want: "false", - }, - { - name: "object", - input: map[string]interface{}{}, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := jsonScalarToString(tt.input) - if (err != nil) != tt.wantErr { - t.Errorf("jsonScalarToString() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("jsonScalarToString() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_executeTemplate(t *testing.T) { - type args struct { - json io.Reader - template string - colorize bool - } - tests := []struct { - name string - args args - wantW string - wantErr bool - }{ - { - name: "color", - args: args{ - json: strings.NewReader(`{}`), - template: `{{color "blue+h" "songs are like tattoos"}}`, - }, - wantW: "\x1b[0;94msongs are like tattoos\x1b[0m", - }, - { - name: "autocolor enabled", - args: args{ - json: strings.NewReader(`{}`), - template: `{{autocolor "red" "stop"}}`, - colorize: true, - }, - wantW: "\x1b[0;31mstop\x1b[0m", - }, - { - name: "autocolor disabled", - args: args{ - json: strings.NewReader(`{}`), - template: `{{autocolor "red" "go"}}`, - }, - wantW: "go", - }, - { - name: "timefmt", - args: args{ - json: strings.NewReader(`{"created_at":"2008-02-25T20:18:33Z"}`), - template: `{{.created_at | timefmt "Mon Jan 2, 2006"}}`, - }, - wantW: "Mon Feb 25, 2008", - }, - { - name: "timeago", - args: args{ - json: strings.NewReader(fmt.Sprintf(`{"created_at":"%s"}`, time.Now().Add(-5*time.Minute).Format(time.RFC3339))), - template: `{{.created_at | timeago}}`, - }, - wantW: "5 minutes ago", - }, - { - name: "pluck", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - {"name": "bug"}, - {"name": "feature request"}, - {"name": "chore"} - ]`)), - template: `{{range(pluck "name" .)}}{{. | printf "%s\n"}}{{end}}`, - }, - wantW: "bug\nfeature request\nchore\n", - }, - { - name: "join", - args: args{ - json: strings.NewReader(`[ "bug", "feature request", "chore" ]`), - template: `{{join "\t" .}}`, - }, - wantW: "bug\tfeature request\tchore", - }, - { - name: "table", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - {"number": 1, "title": "One"}, - {"number": 20, "title": "Twenty"}, - {"number": 3000, "title": "Three thousand"} - ]`)), - template: `{{range .}}{{tablerow (.number | printf "#%v") .title}}{{end}}`, - }, - wantW: heredoc.Doc(`#1 One - #20 Twenty - #3000 Three thousand - `), - }, - { - name: "table with multiline text", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - {"number": 1, "title": "One\ranother line of text"}, - {"number": 20, "title": "Twenty\nanother line of text"}, - {"number": 3000, "title": "Three thousand\r\nanother line of text"} - ]`)), - template: `{{range .}}{{tablerow (.number | printf "#%v") .title}}{{end}}`, - }, - wantW: heredoc.Doc(`#1 One... - #20 Twenty... - #3000 Three thousand... - `), - }, - { - name: "table with mixed value types", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - {"number": 1, "title": null, "float": false}, - {"number": 20.1, "title": "Twenty-ish", "float": true}, - {"number": 3000, "title": "Three thousand", "float": false} - ]`)), - template: `{{range .}}{{tablerow .number .title .float}}{{end}}`, - }, - wantW: heredoc.Doc(`1 false - 20.10 Twenty-ish true - 3000 Three thousand false - `), - }, - { - name: "table with color", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - {"number": 1, "title": "One"} - ]`)), - template: `{{range .}}{{tablerow (.number | color "green") .title}}{{end}}`, - }, - wantW: "\x1b[0;32m1\x1b[0m One\n", - }, - { - name: "table with header and footer", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - {"number": 1, "title": "One"}, - {"number": 2, "title": "Two"} - ]`)), - template: heredoc.Doc(`HEADER - {{range .}}{{tablerow .number .title}}{{end}}FOOTER - `), - }, - wantW: heredoc.Doc(`HEADER - FOOTER - 1 One - 2 Two - `), - }, - { - name: "table with header and footer using endtable", - args: args{ - json: strings.NewReader(heredoc.Doc(`[ - {"number": 1, "title": "One"}, - {"number": 2, "title": "Two"} - ]`)), - template: heredoc.Doc(`HEADER - {{range .}}{{tablerow .number .title}}{{end}}{{tablerender}}FOOTER - `), - }, - wantW: heredoc.Doc(`HEADER - 1 One - 2 Two - FOOTER - `), - }, - { - name: "multiple tables with different columns", - args: args{ - json: strings.NewReader(heredoc.Doc(`{ - "issues": [ - {"number": 1, "title": "One"}, - {"number": 2, "title": "Two"} - ], - "prs": [ - {"number": 3, "title": "Three", "reviewDecision": "REVIEW_REQUESTED"}, - {"number": 4, "title": "Four", "reviewDecision": "CHANGES_REQUESTED"} - ] - }`)), - template: heredoc.Doc(`{{tablerow "ISSUE" "TITLE"}}{{range .issues}}{{tablerow .number .title}}{{end}}{{tablerender}} - {{tablerow "PR" "TITLE" "DECISION"}}{{range .prs}}{{tablerow .number .title .reviewDecision}}{{end}}`), - }, - wantW: heredoc.Docf(`ISSUE TITLE - 1 One - 2 Two - - PR TITLE DECISION - 3 Three REVIEW_REQUESTED - 4 Four CHANGES_REQUESTED - `), - }, - { - name: "truncate", - args: args{ - json: strings.NewReader(`{"title": "This is a long title"}`), - template: `{{truncate 13 .title}}`, - }, - wantW: "This is a ...", - }, - { - name: "truncate with JSON null", - args: args{ - json: strings.NewReader(`{}`), - template: `{{ truncate 13 .title }}`, - }, - wantW: "", - }, - { - name: "truncate with piped JSON null", - args: args{ - json: strings.NewReader(`{}`), - template: `{{ .title | truncate 13 }}`, - }, - wantW: "", - }, - { - name: "truncate with piped JSON null in parenthetical", - args: args{ - json: strings.NewReader(`{}`), - template: `{{ (.title | truncate 13) }}`, - }, - wantW: "", - }, - { - name: "truncate invalid type", - args: args{ - json: strings.NewReader(`{"title": 42}`), - template: `{{ (.title | truncate 13) }}`, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - io, _, w, _ := iostreams.Test() - io.SetColorEnabled(tt.args.colorize) - if err := ExecuteTemplate(io, tt.args.json, tt.args.template); (err != nil) != tt.wantErr { - t.Errorf("executeTemplate() error = %v, wantErr %v", err, tt.wantErr) - return - } - if gotW := w.String(); gotW != tt.wantW { - t.Errorf("executeTemplate() = %q, want %q", gotW, tt.wantW) - } - }) - } -} diff --git a/pkg/markdown/markdown.go b/pkg/markdown/markdown.go index 32313a15c..855559932 100644 --- a/pkg/markdown/markdown.go +++ b/pkg/markdown/markdown.go @@ -1,28 +1,16 @@ package markdown import ( - "os" - "strings" - "github.com/charmbracelet/glamour" + ghMarkdown "github.com/cli/go-gh/pkg/markdown" ) func WithoutIndentation() glamour.TermRendererOption { - overrides := []byte(` - { - "document": { - "margin": 0 - }, - "code_block": { - "margin": 0 - } - }`) - - return glamour.WithStylesFromJSONBytes(overrides) + return ghMarkdown.WithoutIndentation() } func WithWrap(w int) glamour.TermRendererOption { - return glamour.WithWordWrap(w) + return ghMarkdown.WithWrap(w) } type IOStreams interface { @@ -30,33 +18,14 @@ type IOStreams interface { } func WithIO(io IOStreams) glamour.TermRendererOption { - style := os.Getenv("GLAMOUR_STYLE") - if style == "" || style == "auto" { - theme := io.TerminalTheme() - switch theme { - case "light", "dark": - style = theme - default: - style = "notty" - } - } - return glamour.WithStylePath(style) + theme := io.TerminalTheme() + return ghMarkdown.WithTheme(theme) } func WithBaseURL(u string) glamour.TermRendererOption { - return glamour.WithBaseURL(u) + return ghMarkdown.WithBaseURL(u) } func Render(text string, opts ...glamour.TermRendererOption) (string, error) { - // Glamour rendering preserves carriage return characters in code blocks, but - // we need to ensure that no such characters are present in the output. - text = strings.ReplaceAll(text, "\r\n", "\n") - - opts = append(opts, glamour.WithEmoji(), glamour.WithPreservedNewLines()) - tr, err := glamour.NewTermRenderer(opts...) - if err != nil { - return "", err - } - - return tr.Render(text) + return ghMarkdown.Render(text, opts...) } diff --git a/pkg/markdown/markdown_test.go b/pkg/markdown/markdown_test.go deleted file mode 100644 index 77b56d6e9..000000000 --- a/pkg/markdown/markdown_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package markdown - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Render(t *testing.T) { - os.Unsetenv("GLAMOUR_STYLE") - - type input struct { - text string - theme string - } - tests := []struct { - name string - input input - wantsErr bool - }{ - { - name: "light theme", - input: input{ - text: "some text", - theme: "light", - }, - wantsErr: false, - }, - { - name: "dark theme", - input: input{ - text: "some text", - theme: "dark", - }, - wantsErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := Render(tt.input.text, WithIO(terminalThemer(tt.input.theme))) - if tt.wantsErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - }) - } -} - -type terminalThemer string - -func (tt terminalThemer) TerminalTheme() string { - return string(tt) -}