From ef18913e3ccb18f2704ef1d36e9f27d64be64c29 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Tue, 17 Jul 2018 18:43:52 +0800 Subject: [PATCH] First implementation of config file Includes change of CLI args, many of been moved to the config file and those that remain begin with `--` not `-` and may be worded differently. Touches #37 --- interfacer/Gopkg.lock | 64 +++++++++++++++- interfacer/src/browsh/browsh.go | 41 +++-------- interfacer/src/browsh/comms.go | 10 ++- interfacer/src/browsh/config.go | 93 ++++++++++++++++++++++++ interfacer/src/browsh/config_sample.go | 16 ++++ interfacer/src/browsh/firefox.go | 29 ++++---- interfacer/src/browsh/firefox_unix.go | 3 +- interfacer/src/browsh/raw_text_server.go | 5 +- interfacer/src/browsh/tty.go | 3 +- interfacer/src/browsh/ui.go | 3 +- interfacer/test/http-server/setup.go | 5 +- 11 files changed, 216 insertions(+), 56 deletions(-) create mode 100644 interfacer/src/browsh/config.go create mode 100644 interfacer/src/browsh/config_sample.go diff --git a/interfacer/Gopkg.lock b/interfacer/Gopkg.lock index c757659..b92c981 100644 --- a/interfacer/Gopkg.lock +++ b/interfacer/Gopkg.lock @@ -13,6 +13,12 @@ revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" +[[projects]] + name = "github.com/fsnotify/fsnotify" + packages = ["."] + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + version = "v1.4.7" + [[projects]] branch = "master" name = "github.com/gdamore/encoding" @@ -37,18 +43,36 @@ revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" +[[projects]] + branch = "master" + name = "github.com/hashicorp/hcl" + packages = [".","hcl/ast","hcl/parser","hcl/printer","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"] + revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" + [[projects]] name = "github.com/lucasb-eyer/go-colorful" packages = ["."] revision = "345fbb3dbcdb252d9985ee899a84963c0fa24c82" version = "v1.0" +[[projects]] + name = "github.com/magiconair/properties" + packages = ["."] + revision = "c2353362d570a7bfa228149c62842019201cfb71" + version = "v1.8.0" + [[projects]] name = "github.com/mattn/go-runewidth" packages = ["."] revision = "9e777a8366cce605130a531d2cd6363d07ad7317" version = "v0.0.2" +[[projects]] + branch = "master" + name = "github.com/mitchellh/mapstructure" + packages = ["."] + revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" + [[projects]] name = "github.com/onsi/ginkgo" packages = [".","config","internal/codelocation","internal/containernode","internal/failer","internal/leafnodes","internal/remote","internal/spec","internal/spec_iterator","internal/specrunner","internal/suite","internal/testingtproxy","internal/writer","reporters","reporters/stenographer","reporters/stenographer/support/go-colorable","reporters/stenographer/support/go-isatty","types"] @@ -61,6 +85,12 @@ revision = "62bff4df71bdbc266561a0caee19f0594b17c240" version = "v1.4.0" +[[projects]] + name = "github.com/pelletier/go-toml" + packages = ["."] + revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" + version = "v1.2.0" + [[projects]] name = "github.com/pkg/errors" packages = ["."] @@ -79,6 +109,36 @@ packages = ["."] revision = "e180dbdc8da04c4fa04272e875ce64949f38bd3e" +[[projects]] + name = "github.com/spf13/afero" + packages = [".","mem"] + revision = "787d034dfe70e44075ccc060d346146ef53270ad" + version = "v1.1.1" + +[[projects]] + name = "github.com/spf13/cast" + packages = ["."] + revision = "8965335b8c7107321228e3e3702cab9832751bac" + version = "v1.2.0" + +[[projects]] + branch = "master" + name = "github.com/spf13/jwalterweatherman" + packages = ["."] + revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394" + +[[projects]] + name = "github.com/spf13/pflag" + packages = ["."] + revision = "583c0c0531f06d5278b7d917446061adc344b5cd" + version = "v1.0.1" + +[[projects]] + name = "github.com/spf13/viper" + packages = ["."] + revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736" + version = "v1.0.2" + [[projects]] name = "github.com/stretchr/testify" packages = ["assert","require"] @@ -105,7 +165,7 @@ [[projects]] name = "golang.org/x/text" - packages = ["encoding","encoding/charmap","encoding/htmlindex","encoding/internal","encoding/internal/identifier","encoding/japanese","encoding/korean","encoding/simplifiedchinese","encoding/traditionalchinese","encoding/unicode","internal/gen","internal/tag","internal/utf8internal","language","runes","transform","unicode/cldr"] + packages = ["encoding","encoding/charmap","encoding/htmlindex","encoding/internal","encoding/internal/identifier","encoding/japanese","encoding/korean","encoding/simplifiedchinese","encoding/traditionalchinese","encoding/unicode","internal/gen","internal/tag","internal/triegen","internal/ucd","internal/utf8internal","language","runes","transform","unicode/cldr","unicode/norm"] revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" @@ -118,6 +178,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "acff811653ac14fa3fc60de5ffe1b15b5889b5f93a73863398bff1585a675589" + inputs-digest = "a758e47b231056184a2433d9870401e6d2a15fc69c7f9b35780bd57fbc0fc24e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index e564402..4ab5302 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -2,7 +2,6 @@ package browsh import ( "encoding/base64" - "flag" "fmt" "io/ioutil" "os" @@ -17,7 +16,8 @@ import ( "github.com/gdamore/tcell" "github.com/go-errors/errors" - "github.com/shibukawa/configdir" + "github.com/spf13/viper" + "github.com/spf13/pflag" ) var ( @@ -35,20 +35,6 @@ var ( ****/////////////////// ********/////////////// ***********************` - webSocketPort = flag.String("websocket-port", "3334", "Web socket service address") - firefoxBinary = flag.String("firefox", "firefox", "Path to Firefox executable") - isFFGui = flag.Bool("with-gui", false, "Don't use headless Firefox") - isUseExistingFirefox = flag.Bool("use-existing-ff", false, "Whether Browsh should launch Firefox or not") - useFFProfile = flag.String("ff-profile", "default", "Firefox profile to use") - isDebug = flag.Bool("debug", false, "Log to ./debug.log") - timeLimit = flag.Int("time-limit", 0, "Kill Browsh after the specified number of seconds") - // StartupURL is the URL of the first tab at boot - StartupURL = flag.String("startup-url", "https://google.com", "URL to launch at startup") - // IsHTTPServer needs to be exported for use in tests - IsHTTPServer = flag.Bool("http-server", false, "Run as an HTTP service") - // HTTPServerPort also needs to be exported for use in tests - HTTPServerPort = flag.String("http-server-port", "4333", "HTTP server address") - httpServerBind = flag.String("http-server-bind", "0.0.0.0", "HTTP server binding address") // IsTesting is used in tests, so it needs to be exported IsTesting = false logfile string @@ -75,7 +61,7 @@ func Log(msg string) { if !*isDebug { return } - if *IsHTTPServer && !IsTesting { + if viper.GetBool("http-server-mode") && !IsTesting { fmt.Println(msg) } else { f, oErr := os.OpenFile(logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) @@ -98,10 +84,15 @@ func initialise() { if *isDebug { setupLogging() } + loadConfig() } // Shutdown tries its best to cleanly shutdown browsh and the associated browser func Shutdown(err error) { + if *isDebug { + out := err.(*errors.Error).ErrorStack() + Log(fmt.Sprintf(out)) + } exitCode := 0 if screen != nil { screen.Fini() @@ -110,10 +101,6 @@ func Shutdown(err error) { exitCode = 1 println(err.Error()) } - if *isDebug { - out := err.(*errors.Error).ErrorStack() - Log(fmt.Sprintf(out)) - } os.Exit(exitCode) } @@ -141,14 +128,6 @@ func saveScreenshot(base64String string) { file.Close() } -// Gets a cross-platform path to store Browsh config -func getConfigFolder() string { - configDirs := configdir.New("browsh", "firefox_profile") - folders := configDirs.QueryFolders(configdir.Global) - folders[0].MkdirAll() - return folders[0].Path -} - // Shell provides nice and easy shell commands func Shell(command string) string { parts := strings.Fields(command) @@ -210,8 +189,8 @@ func ttyEntry() { // MainEntry decides between running Browsh as a CLI app or as an HTTP web server func MainEntry() { - flag.Parse() - if *IsHTTPServer { + pflag.Parse() + if viper.GetBool("http-server-mode") { HTTPServerStart() } else { ttyEntry() diff --git a/interfacer/src/browsh/comms.go b/interfacer/src/browsh/comms.go index a3e1ee0..5527e4e 100644 --- a/interfacer/src/browsh/comms.go +++ b/interfacer/src/browsh/comms.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/gorilla/websocket" + "github.com/spf13/viper" ) var ( @@ -27,7 +28,8 @@ type incomingRawText struct { func startWebSocketServer() { serverMux := http.NewServeMux() serverMux.HandleFunc("/", webSocketServer) - if err := http.ListenAndServe(":"+*webSocketPort, serverMux); err != nil { + port := viper.GetString("browsh.websocket-port") + if err := http.ListenAndServe(":"+port, serverMux); err != nil { Shutdown(err) } } @@ -61,7 +63,7 @@ func webSocketReader(ws *websocket.Conn) { func handleWebextensionCommand(message []byte) { parts := strings.Split(string(message), ",") command := parts[0] - if *IsHTTPServer { + if viper.GetBool("http-server-mode") { handleRawFrameTextCommands(parts) return } @@ -139,12 +141,12 @@ func webSocketServer(w http.ResponseWriter, r *http.Request) { isConnectedToWebExtension = true go webSocketWriter(ws) go webSocketReader(ws) - if *IsHTTPServer { + if viper.GetBool("http-server-mode") { sendMessageToWebExtension("/raw_text_mode") } else { sendTtySize() } // For some reason, using Firefox's CLI arg `--url https://google.com` doesn't consistently // work. So we do it here instead. - sendMessageToWebExtension("/new_tab," + *StartupURL) + sendMessageToWebExtension("/new_tab," + viper.GetString("startup-url")) } diff --git a/interfacer/src/browsh/config.go b/interfacer/src/browsh/config.go new file mode 100644 index 0000000..b3bd3cf --- /dev/null +++ b/interfacer/src/browsh/config.go @@ -0,0 +1,93 @@ +package browsh + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "bytes" + + "github.com/shibukawa/configdir" + "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +var ( + configFilename = "config.toml" + + isDebug = pflag.Bool("debug", false, "Log to ./debug.log") + timeLimit = pflag.Int("time-limit", 0, "Kill Browsh after the specified number of seconds") + _ = pflag.Bool("http-server-mode", false, "Run as an HTTP service") + + _ = pflag.String("startup-url", "https://google.com", "URL to launch at startup") + _ = pflag.String("firefox.path", "firefox", "Path to Firefox executable") + _ = pflag.Bool("firefox.with-gui", false, "Don't use headless Firefox") + _ = pflag.Bool("firefox.use-existing", false, "Whether Browsh should launch Firefox or not") +) + +func getConfigNamespace() string { + if IsTesting { + return "browsh-testing" + } + return "browsh" +} + +// Gets a cross-platform path to a folder containing Browsh config +func getConfigDir() string { + marker := "browsh-settings" + // configdir has no other option but to have a nested folder + configDirs := configdir.New(getConfigNamespace(), marker) + folders := configDirs.QueryFolders(configdir.Global) + // Delete the previously enforced nested folder + path := strings.Trim(folders[0].Path, marker) + os.MkdirAll(path, os.ModePerm) + ensureConfigFile(path) + return path +} + +// Copy the sample config file if the user doesn't already have a config file +func ensureConfigFile(path string) { + fullPath := filepath.Join(path, configFilename) + if _, err := os.Stat(fullPath); os.IsNotExist(err) { + file, err := os.Create(fullPath) + if err != nil { + Shutdown(err) + } + defer file.Close() + _, err = file.WriteString(configSample) + if err != nil { + Shutdown(err) + } + } +} + +// Gets a cross-platform path to store a Browsh-specific Firefox profile +func getFirefoxProfilePath() string { + configDirs := configdir.New(getConfigNamespace(), "firefox_profile") + folders := configDirs.QueryFolders(configdir.Global) + folders[0].MkdirAll() + return folders[0].Path +} + +func loadConfig() { + dir := getConfigDir() + fullPath := filepath.Join(dir, configFilename) + Log("Looking in " + fullPath + " for config.") + viper.SetConfigType("toml") + viper.SetConfigName(strings.Trim(configFilename, ".toml")) + viper.AddConfigPath(dir) + viper.AddConfigPath(".") + // First load the sample config in case the user hasn't updated any new fields + err := viper.ReadConfig(bytes.NewBuffer([]byte(configSample))) + if err != nil { + Shutdown(err) + } + // Then load the users own config file, overwriting the sample config + err = viper.MergeInConfig() + if err != nil { + Shutdown(err) + } + viper.BindPFlags(pflag.CommandLine) + Log("Using the folowing config values:") + Log(fmt.Sprintf("%v", viper.AllSettings())) +} diff --git a/interfacer/src/browsh/config_sample.go b/interfacer/src/browsh/config_sample.go new file mode 100644 index 0000000..31f4062 --- /dev/null +++ b/interfacer/src/browsh/config_sample.go @@ -0,0 +1,16 @@ +package browsh + +var configSample = +`[browsh] +websocket-port = 3334 + +[firefox] +path = "firefox" +profile = "default" +use-existing = false +with-gui = false + +[http-server] +port = 4333 +bind = "0.0.0.0" +` diff --git a/interfacer/src/browsh/firefox.go b/interfacer/src/browsh/firefox.go index 0893297..7481f67 100644 --- a/interfacer/src/browsh/firefox.go +++ b/interfacer/src/browsh/firefox.go @@ -15,6 +15,7 @@ import ( "github.com/gdamore/tcell" "github.com/go-errors/errors" + "github.com/spf13/viper" ) var ( @@ -58,18 +59,19 @@ func startHeadlessFirefox() { ensureFirefoxBinary() ensureFirefoxVersion() args := []string{"--marionette"} - if !*isFFGui { + if !viper.GetBool("firefox.with-gui") { args = append(args, "--headless") } - if *useFFProfile != "default" { - Log("Using profile: " + *useFFProfile) - args = append(args, "-P", *useFFProfile) + profile := viper.GetString("firefox.profile") + if profile != "default" { + Log("Using profile: " + profile) + args = append(args, "-P", profile) } else { - profilePath := getConfigFolder() + profilePath := getFirefoxProfilePath() Log("Using default profile at: " + profilePath) args = append(args, "--profile", profilePath) } - firefoxProcess := exec.Command(*firefoxBinary, args...) + firefoxProcess := exec.Command(viper.GetString("firefox.path"), args...) defer firefoxProcess.Process.Kill() stdout, err := firefoxProcess.StdoutPipe() if err != nil { @@ -96,18 +98,19 @@ func checkIfFirefoxIsAlreadyRunning() { } func ensureFirefoxBinary() { - if *firefoxBinary == "firefox" { + path := viper.GetString("firefox.path") + if path == "firefox" { switch runtime.GOOS { case "windows": - *firefoxBinary = getFirefoxPath() + path = getFirefoxPath() case "darwin": - *firefoxBinary = "/Applications/Firefox.app/Contents/MacOS/firefox" + path = "/Applications/Firefox.app/Contents/MacOS/firefox" default: - *firefoxBinary = getFirefoxPath() + path = getFirefoxPath() } } - if _, err := os.Stat(*firefoxBinary); os.IsNotExist(err) { - Shutdown(errors.New("Firefox binary not found: " + *firefoxBinary)) + if _, err := os.Stat(path); os.IsNotExist(err) { + Shutdown(errors.New("Firefox binary not found: " + path)) } } @@ -292,7 +295,7 @@ func setupFirefox() { } func startFirefox() { - if !*isUseExistingFirefox { + if !viper.GetBool("firefox.use-existing") { writeString(0, 16, "Waiting for Firefox to connect...", tcell.StyleDefault) if IsTesting { writeString(0, 17, "TEST MODE", tcell.StyleDefault) diff --git a/interfacer/src/browsh/firefox_unix.go b/interfacer/src/browsh/firefox_unix.go index 0838a12..c20b70a 100644 --- a/interfacer/src/browsh/firefox_unix.go +++ b/interfacer/src/browsh/firefox_unix.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/go-errors/errors" + "github.com/spf13/viper" ) func getFirefoxPath() string { @@ -17,7 +18,7 @@ func ensureFirefoxVersion() { if runtime.GOOS == "windows" { return } - output := Shell(*firefoxBinary + " --version") + output := Shell(viper.GetString("firefox.path") + " --version") pieces := strings.Split(output, " ") version := pieces[len(pieces)-1] if versionOrdinal(version) < versionOrdinal("57") { diff --git a/interfacer/src/browsh/raw_text_server.go b/interfacer/src/browsh/raw_text_server.go index 17ec504..bfbbd61 100644 --- a/interfacer/src/browsh/raw_text_server.go +++ b/interfacer/src/browsh/raw_text_server.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/spf13/viper" "github.com/NYTimes/gziphandler" "github.com/ulule/limiter" "github.com/ulule/limiter/drivers/middleware/stdlib" @@ -31,11 +32,13 @@ func HTTPServerStart() { startFirefox() go startWebSocketServer() Log("Starting Browsh HTTP server") + bind := viper.GetString("http-server.bind") + port := viper.GetString("http-server.port") serverMux := http.NewServeMux() uncompressed := http.HandlerFunc(handleHTTPServerRequest) limiterMiddleware := setupRateLimiter() serverMux.Handle("/", limiterMiddleware.Handler(gziphandler.GzipHandler(uncompressed))) - if err := http.ListenAndServe(*httpServerBind+":"+*HTTPServerPort, &slashFix{serverMux}); err != nil { + if err := http.ListenAndServe(bind+":"+port, &slashFix{serverMux}); err != nil { Shutdown(err) } } diff --git a/interfacer/src/browsh/tty.go b/interfacer/src/browsh/tty.go index ade100d..78affbf 100644 --- a/interfacer/src/browsh/tty.go +++ b/interfacer/src/browsh/tty.go @@ -7,6 +7,7 @@ import ( "github.com/gdamore/tcell" "github.com/go-errors/errors" + "github.com/spf13/viper" ) var ( @@ -90,7 +91,7 @@ func handleUserKeyPress(ev *tcell.EventKey) { } func quitBrowsh() { - if !*isUseExistingFirefox { + if !viper.GetBool("firefox.use-existing") { quitFirefox() } Shutdown(errors.New("normal")) diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 543cb41..13ab782 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -1,6 +1,7 @@ package browsh import ( + "github.com/spf13/viper" "github.com/gdamore/tcell" ) @@ -26,7 +27,7 @@ func renderUI() { // the browser that must be done through the webextension. func writeString(x, y int, str string, style tcell.Style) { xOriginal := x - if *IsHTTPServer { + if viper.GetBool("http-server-mode") { Log(str) return } diff --git a/interfacer/test/http-server/setup.go b/interfacer/test/http-server/setup.go index 50f6596..954f7fd 100644 --- a/interfacer/test/http-server/setup.go +++ b/interfacer/test/http-server/setup.go @@ -8,6 +8,7 @@ import ( ginkgo "github.com/onsi/ginkgo" + "github.com/spf13/viper" "browsh/interfacer/src/browsh" ) @@ -22,12 +23,12 @@ func startStaticFileServer() { func startBrowsh() { browsh.IsTesting = true - *browsh.IsHTTPServer = true + viper.Set("http-server-mode", true) browsh.HTTPServerStart() } func getPath(path string, mode string) string { - browshServiceBase := "http://localhost:" + *browsh.HTTPServerPort + browshServiceBase := "http://localhost:" + viper.GetString("http-server.port") staticFileServerBase := "http://localhost:" + staticFileServerPort fullBase := browshServiceBase + "/" + staticFileServerBase client := &http.Client{}