diff --git a/deps.bzl b/deps.bzl index fe8860705ee2..1a96cf8d92e4 100644 --- a/deps.bzl +++ b/deps.bzl @@ -3636,3 +3636,9 @@ def prysm_deps(): sum = "h1:W0LEBv82YCGEtcmPA3uNZBI33/qF//HAAs3MawDjRa0=", version = "v0.0.0-20160331181800-b5bfa59ec0ad", ) + go_repository( + name = "com_github_google_shlex", + importpath = "github.com/google/shlex", + sum = "h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=", + version = "v0.0.0-20191202100458-e7afc7fbc510", + ) diff --git a/go.mod b/go.mod index bf3ad8e6e5c5..197a8959a404 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/golang/protobuf v1.4.2 github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.1.1 github.com/gorilla/websocket v1.4.2 github.com/graph-gophers/graphql-go v0.0.0-20200309224638-dae41bde9ef9 // indirect diff --git a/go.sum b/go.sum index b99f1f93ee50..91832b959b84 100644 --- a/go.sum +++ b/go.sum @@ -345,6 +345,8 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/shared/browser/BUILD.bazel b/shared/browser/BUILD.bazel new file mode 100644 index 000000000000..267ac29fa996 --- /dev/null +++ b/shared/browser/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") +load("@prysm//tools/go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["browser.go"], + importpath = "github.com/prysmaticlabs/prysm/shared/browser", + visibility = ["//visibility:public"], + deps = ["@com_github_google_shlex//:go_default_library"], +) + +go_test( + name = "go_default_test", + srcs = ["browser_test.go"], + embed = [":go_default_library"], +) diff --git a/shared/browser/browser.go b/shared/browser/browser.go new file mode 100644 index 000000000000..48b1982db20d --- /dev/null +++ b/shared/browser/browser.go @@ -0,0 +1,57 @@ +/** +Package from the official Github CLI https://github.com/cli/cli/blob/f30bc5bc64f9c3a839e39713adab48790264119c/pkg/browser/browser.go +All rights reserved to the package authors, respectively. MIT License. See https://github.com/cli/cli/blob/trunk/LICENSE +*/ +package browser + +import ( + "os" + "os/exec" + "runtime" + "strings" + + "github.com/google/shlex" +) + +// Command produces an exec.Cmd respecting runtime.GOOS and $BROWSER environment variable +func Command(url string) (*exec.Cmd, error) { + launcher := os.Getenv("BROWSER") + if launcher != "" { + return FromLauncher(launcher, url) + } + return ForOS(runtime.GOOS, url), nil +} + +// ForOS produces an exec.Cmd to open the web browser for different OS +func ForOS(goos, url string) *exec.Cmd { + exe := "open" + var args []string + switch goos { + case "darwin": + args = append(args, url) + case "windows": + exe = "cmd" + r := strings.NewReplacer("&", "^&") + args = append(args, "/c", "start", r.Replace(url)) + default: + exe = "xdg-open" + args = append(args, url) + } + + cmd := exec.Command(exe, args...) + cmd.Stderr = os.Stderr + return cmd +} + +// FromLauncher parses the launcher string based on shell splitting rules +func FromLauncher(launcher, url string) (*exec.Cmd, error) { + args, err := shlex.Split(launcher) + if err != nil { + return nil, err + } + + args = append(args, url) + cmd := exec.Command(args[0], args[1:]...) + cmd.Stderr = os.Stderr + return cmd, nil +} diff --git a/shared/browser/browser_test.go b/shared/browser/browser_test.go new file mode 100644 index 000000000000..fedcc76b9d6c --- /dev/null +++ b/shared/browser/browser_test.go @@ -0,0 +1,54 @@ +/** +Package from the official Github CLI https://github.com/cli/cli/blob/f30bc5bc64f9c3a839e39713adab48790264119c/pkg/browser/browser.go +All rights reserved to the package authors, respectively. MIT License. See https://github.com/cli/cli/blob/trunk/LICENSE +*/ +package browser + +import ( + "reflect" + "testing" +) + +func TestForOS(t *testing.T) { + type args struct { + goos string + url string + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "macOS", + args: args{ + goos: "darwin", + url: "https://example.com/path?a=1&b=2", + }, + want: []string{"open", "https://example.com/path?a=1&b=2"}, + }, + { + name: "Linux", + args: args{ + goos: "linux", + url: "https://example.com/path?a=1&b=2", + }, + want: []string{"xdg-open", "https://example.com/path?a=1&b=2"}, + }, + { + name: "Windows", + args: args{ + goos: "windows", + url: "https://example.com/path?a=1&b=2&c=3", + }, + want: []string{"cmd", "/c", "start", "https://example.com/path?a=1^&b=2^&c=3"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if cmd := ForOS(tt.args.goos, tt.args.url); !reflect.DeepEqual(cmd.Args, tt.want) { + t.Errorf("ForOS() = %v, want %v", cmd.Args, tt.want) + } + }) + } +} diff --git a/validator/web/BUILD.bazel b/validator/web/BUILD.bazel index db9385ec95ff..3ae883064de0 100644 --- a/validator/web/BUILD.bazel +++ b/validator/web/BUILD.bazel @@ -15,6 +15,7 @@ go_library( visibility = ["//validator:__subpackages__"], deps = [ "//shared:go_default_library", + "//shared/browser:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", ], ) diff --git a/validator/web/handler.go b/validator/web/handler.go index e0bead6f1e50..59c080eccf57 100644 --- a/validator/web/handler.go +++ b/validator/web/handler.go @@ -5,8 +5,6 @@ import ( "net/http" "net/url" "path" - - log "github.com/sirupsen/logrus" ) const prefix = "external/prysm_web_ui" diff --git a/validator/web/server.go b/validator/web/server.go index 6cfa28b40088..2eab9963dc42 100644 --- a/validator/web/server.go +++ b/validator/web/server.go @@ -6,10 +6,14 @@ import ( "time" "github.com/prysmaticlabs/prysm/shared" - log "github.com/sirupsen/logrus" + "github.com/prysmaticlabs/prysm/shared/browser" + "github.com/sirupsen/logrus" ) -var _ = shared.Service(&Server{}) +var ( + _ = shared.Service(&Server{}) + log = logrus.WithField("prefix", "prysm-web") +) // Server for the Prysm Web UI. type Server struct { @@ -37,6 +41,16 @@ func (s *Server) Start() { log.WithError(err).Error("Failed to start validator web server") } }() + time.Sleep(time.Second * 1) + cmd, err := browser.Command("http://" + s.http.Addr) + if err != nil { + log.WithError(err).Errorf("Could not open Prysm web UI in browser") + return + } + if err := cmd.Run(); err != nil { + log.WithError(err).Errorf("Could not open Prysm web UI in browser") + return + } } // Stop the web server gracefully with 1s timeout.