From d62624a3f8057bdff8bc9cbac055d2a9fe596901 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Thu, 27 Jun 2024 14:59:37 -0700 Subject: [PATCH 1/3] Open browser automatically during login command --- cmd/login/login.go | 15 ++--- go.mod | 1 + go.sum | 2 + internal/login/login.go | 2 +- internal/login/login_test.go | 2 +- vendor/github.com/pkg/browser/LICENSE | 23 ++++++++ vendor/github.com/pkg/browser/README.md | 55 ++++++++++++++++++ vendor/github.com/pkg/browser/browser.go | 57 +++++++++++++++++++ .../github.com/pkg/browser/browser_darwin.go | 5 ++ .../github.com/pkg/browser/browser_freebsd.go | 14 +++++ .../github.com/pkg/browser/browser_linux.go | 21 +++++++ .../github.com/pkg/browser/browser_netbsd.go | 14 +++++ .../github.com/pkg/browser/browser_openbsd.go | 14 +++++ .../pkg/browser/browser_unsupported.go | 12 ++++ .../github.com/pkg/browser/browser_windows.go | 7 +++ vendor/modules.txt | 3 + 16 files changed, 238 insertions(+), 9 deletions(-) create mode 100644 vendor/github.com/pkg/browser/LICENSE create mode 100644 vendor/github.com/pkg/browser/README.md create mode 100644 vendor/github.com/pkg/browser/browser.go create mode 100644 vendor/github.com/pkg/browser/browser_darwin.go create mode 100644 vendor/github.com/pkg/browser/browser_freebsd.go create mode 100644 vendor/github.com/pkg/browser/browser_linux.go create mode 100644 vendor/github.com/pkg/browser/browser_netbsd.go create mode 100644 vendor/github.com/pkg/browser/browser_openbsd.go create mode 100644 vendor/github.com/pkg/browser/browser_unsupported.go create mode 100644 vendor/github.com/pkg/browser/browser_windows.go diff --git a/cmd/login/login.go b/cmd/login/login.go index 314d18c0..d3aec8db 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/pkg/browser" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -68,18 +69,18 @@ func run(client login.Client) func(*cobra.Command, []string) error { return err } + fullURL := fmt.Sprintf("%s/%s", viper.GetString(cliflags.BaseURIFlag), deviceAuthorization.VerificationURI) var b strings.Builder b.WriteString(fmt.Sprintf("Your code is %s\n", deviceAuthorization.UserCode)) b.WriteString("This code verifies your authentication with LaunchDarkly.\n") - b.WriteString( - fmt.Sprintf( - "Press Enter to open the browser or visit %s/%s (^C to quit)\n", - viper.GetString(cliflags.BaseURIFlag), - deviceAuthorization.VerificationURI, - ), - ) + b.WriteString(fmt.Sprintf("If your browser did not open to confirm your login, visit %s\n", fullURL)) fmt.Fprintln(cmd.OutOrStdout(), b.String()) + err = browser.OpenURL(fullURL) + if err != nil { + return err + } + deviceAuthorizationToken, err := login.FetchToken( client, deviceAuthorization.DeviceCode, diff --git a/go.mod b/go.mod index d874a332..efd0b03e 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/launchdarkly/api-client-go/v14 v14.0.0 github.com/mitchellh/go-homedir v1.1.0 github.com/muesli/reflow v0.3.0 + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 1b1ea4f7..8c65725a 100644 --- a/go.sum +++ b/go.sum @@ -199,6 +199,8 @@ github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6 github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/internal/login/login.go b/internal/login/login.go index 8f145272..541e47ab 100644 --- a/internal/login/login.go +++ b/internal/login/login.go @@ -147,7 +147,7 @@ func FetchToken( case "expired_token": return DeviceAuthorizationToken{}, errors.NewError("Your request has expired. Please try logging in again.") default: - return DeviceAuthorizationToken{}, errors.NewErrorWrapped("We cannot complete your request.", err) + return DeviceAuthorizationToken{}, errors.NewErrorWrapped(fmt.Sprintf("We cannot complete your request: %s", e.Message), err) } time.Sleep(interval) } diff --git a/internal/login/login_test.go b/internal/login/login_test.go index 0a94be4f..8cf5d4eb 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -113,7 +113,7 @@ func TestFetchToken_WithError(t *testing.T) { }, "with an error response": { errCode: "error_code", - expectedErr: "We cannot complete your request.", + expectedErr: "We cannot complete your request: error message", }, } for name, tt := range tests { diff --git a/vendor/github.com/pkg/browser/LICENSE b/vendor/github.com/pkg/browser/LICENSE new file mode 100644 index 00000000..65f78fb6 --- /dev/null +++ b/vendor/github.com/pkg/browser/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2014, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/browser/README.md b/vendor/github.com/pkg/browser/README.md new file mode 100644 index 00000000..72b1976e --- /dev/null +++ b/vendor/github.com/pkg/browser/README.md @@ -0,0 +1,55 @@ + +# browser + import "github.com/pkg/browser" + +Package browser provides helpers to open files, readers, and urls in a browser window. + +The choice of which browser is started is entirely client dependant. + + + + + +## Variables +``` go +var Stderr io.Writer = os.Stderr +``` +Stderr is the io.Writer to which executed commands write standard error. + +``` go +var Stdout io.Writer = os.Stdout +``` +Stdout is the io.Writer to which executed commands write standard output. + + +## func OpenFile +``` go +func OpenFile(path string) error +``` +OpenFile opens new browser window for the file path. + + +## func OpenReader +``` go +func OpenReader(r io.Reader) error +``` +OpenReader consumes the contents of r and presents the +results in a new browser window. + + +## func OpenURL +``` go +func OpenURL(url string) error +``` +OpenURL opens a new browser window pointing to url. + + + + + + + + + +- - - +Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) diff --git a/vendor/github.com/pkg/browser/browser.go b/vendor/github.com/pkg/browser/browser.go new file mode 100644 index 00000000..d7969d74 --- /dev/null +++ b/vendor/github.com/pkg/browser/browser.go @@ -0,0 +1,57 @@ +// Package browser provides helpers to open files, readers, and urls in a browser window. +// +// The choice of which browser is started is entirely client dependant. +package browser + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" +) + +// Stdout is the io.Writer to which executed commands write standard output. +var Stdout io.Writer = os.Stdout + +// Stderr is the io.Writer to which executed commands write standard error. +var Stderr io.Writer = os.Stderr + +// OpenFile opens new browser window for the file path. +func OpenFile(path string) error { + path, err := filepath.Abs(path) + if err != nil { + return err + } + return OpenURL("file://" + path) +} + +// OpenReader consumes the contents of r and presents the +// results in a new browser window. +func OpenReader(r io.Reader) error { + f, err := ioutil.TempFile("", "browser.*.html") + if err != nil { + return fmt.Errorf("browser: could not create temporary file: %v", err) + } + if _, err := io.Copy(f, r); err != nil { + f.Close() + return fmt.Errorf("browser: caching temporary file failed: %v", err) + } + if err := f.Close(); err != nil { + return fmt.Errorf("browser: caching temporary file failed: %v", err) + } + return OpenFile(f.Name()) +} + +// OpenURL opens a new browser window pointing to url. +func OpenURL(url string) error { + return openBrowser(url) +} + +func runCmd(prog string, args ...string) error { + cmd := exec.Command(prog, args...) + cmd.Stdout = Stdout + cmd.Stderr = Stderr + return cmd.Run() +} diff --git a/vendor/github.com/pkg/browser/browser_darwin.go b/vendor/github.com/pkg/browser/browser_darwin.go new file mode 100644 index 00000000..8507cf7c --- /dev/null +++ b/vendor/github.com/pkg/browser/browser_darwin.go @@ -0,0 +1,5 @@ +package browser + +func openBrowser(url string) error { + return runCmd("open", url) +} diff --git a/vendor/github.com/pkg/browser/browser_freebsd.go b/vendor/github.com/pkg/browser/browser_freebsd.go new file mode 100644 index 00000000..4fc7ff07 --- /dev/null +++ b/vendor/github.com/pkg/browser/browser_freebsd.go @@ -0,0 +1,14 @@ +package browser + +import ( + "errors" + "os/exec" +) + +func openBrowser(url string) error { + err := runCmd("xdg-open", url) + if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound { + return errors.New("xdg-open: command not found - install xdg-utils from ports(8)") + } + return err +} diff --git a/vendor/github.com/pkg/browser/browser_linux.go b/vendor/github.com/pkg/browser/browser_linux.go new file mode 100644 index 00000000..d26cdddf --- /dev/null +++ b/vendor/github.com/pkg/browser/browser_linux.go @@ -0,0 +1,21 @@ +package browser + +import ( + "os/exec" + "strings" +) + +func openBrowser(url string) error { + providers := []string{"xdg-open", "x-www-browser", "www-browser"} + + // There are multiple possible providers to open a browser on linux + // One of them is xdg-open, another is x-www-browser, then there's www-browser, etc. + // Look for one that exists and run it + for _, provider := range providers { + if _, err := exec.LookPath(provider); err == nil { + return runCmd(provider, url) + } + } + + return &exec.Error{Name: strings.Join(providers, ","), Err: exec.ErrNotFound} +} diff --git a/vendor/github.com/pkg/browser/browser_netbsd.go b/vendor/github.com/pkg/browser/browser_netbsd.go new file mode 100644 index 00000000..65a5e5a2 --- /dev/null +++ b/vendor/github.com/pkg/browser/browser_netbsd.go @@ -0,0 +1,14 @@ +package browser + +import ( + "errors" + "os/exec" +) + +func openBrowser(url string) error { + err := runCmd("xdg-open", url) + if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound { + return errors.New("xdg-open: command not found - install xdg-utils from pkgsrc(7)") + } + return err +} diff --git a/vendor/github.com/pkg/browser/browser_openbsd.go b/vendor/github.com/pkg/browser/browser_openbsd.go new file mode 100644 index 00000000..4fc7ff07 --- /dev/null +++ b/vendor/github.com/pkg/browser/browser_openbsd.go @@ -0,0 +1,14 @@ +package browser + +import ( + "errors" + "os/exec" +) + +func openBrowser(url string) error { + err := runCmd("xdg-open", url) + if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound { + return errors.New("xdg-open: command not found - install xdg-utils from ports(8)") + } + return err +} diff --git a/vendor/github.com/pkg/browser/browser_unsupported.go b/vendor/github.com/pkg/browser/browser_unsupported.go new file mode 100644 index 00000000..7c5c17d3 --- /dev/null +++ b/vendor/github.com/pkg/browser/browser_unsupported.go @@ -0,0 +1,12 @@ +// +build !linux,!windows,!darwin,!openbsd,!freebsd,!netbsd + +package browser + +import ( + "fmt" + "runtime" +) + +func openBrowser(url string) error { + return fmt.Errorf("openBrowser: unsupported operating system: %v", runtime.GOOS) +} diff --git a/vendor/github.com/pkg/browser/browser_windows.go b/vendor/github.com/pkg/browser/browser_windows.go new file mode 100644 index 00000000..63e19295 --- /dev/null +++ b/vendor/github.com/pkg/browser/browser_windows.go @@ -0,0 +1,7 @@ +package browser + +import "golang.org/x/sys/windows" + +func openBrowser(url string) error { + return windows.ShellExecute(0, nil, windows.StringToUTF16Ptr(url), nil, nil, windows.SW_SHOWNORMAL) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ba1c104e..5705d913 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -187,6 +187,9 @@ github.com/pelletier/go-toml/v2/unstable # github.com/perimeterx/marshmallow v1.1.5 ## explicit; go 1.17 github.com/perimeterx/marshmallow +# github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c +## explicit; go 1.14 +github.com/pkg/browser # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors From 0454dd4efa5cc7ac33f52ec41ed78de8ee82c506 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Thu, 27 Jun 2024 15:01:00 -0700 Subject: [PATCH 2/3] Only show URL if browser doesn't open --- cmd/login/login.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/login/login.go b/cmd/login/login.go index d3aec8db..e056617b 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -73,13 +73,14 @@ func run(client login.Client) func(*cobra.Command, []string) error { var b strings.Builder b.WriteString(fmt.Sprintf("Your code is %s\n", deviceAuthorization.UserCode)) b.WriteString("This code verifies your authentication with LaunchDarkly.\n") - b.WriteString(fmt.Sprintf("If your browser did not open to confirm your login, visit %s\n", fullURL)) - fmt.Fprintln(cmd.OutOrStdout(), b.String()) err = browser.OpenURL(fullURL) - if err != nil { - return err + if err == nil { + b.WriteString("Please check your browser to confirm your login.\n") + } else { + b.WriteString(fmt.Sprintf("If your browser did not open to confirm your login, visit %s.\n", fullURL)) } + fmt.Fprintln(cmd.OutOrStdout(), b.String()) deviceAuthorizationToken, err := login.FetchToken( client, From 47567a6654dfcfc7d48291a26e26e51f97739436 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Thu, 27 Jun 2024 16:21:09 -0700 Subject: [PATCH 3/3] Update login language --- cmd/login/login.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cmd/login/login.go b/cmd/login/login.go index e056617b..450e7012 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -73,15 +73,12 @@ func run(client login.Client) func(*cobra.Command, []string) error { var b strings.Builder b.WriteString(fmt.Sprintf("Your code is %s\n", deviceAuthorization.UserCode)) b.WriteString("This code verifies your authentication with LaunchDarkly.\n") - - err = browser.OpenURL(fullURL) - if err == nil { - b.WriteString("Please check your browser to confirm your login.\n") - } else { - b.WriteString(fmt.Sprintf("If your browser did not open to confirm your login, visit %s.\n", fullURL)) - } + b.WriteString(fmt.Sprintf("Opening your browser to %s to finish verifying your login.\n", fullURL)) + b.WriteString("If your browser does not open automatically, you can paste the above URL into your browser.") fmt.Fprintln(cmd.OutOrStdout(), b.String()) + _ = browser.OpenURL(fullURL) + deviceAuthorizationToken, err := login.FetchToken( client, deviceAuthorization.DeviceCode,