package cmd import ( "fmt" neturl "net/url" "os/exec" "runtime" "strings" "github.com/spf13/cobra" ) var browsePrintOnly bool func init() { browseCmd := &cobra.Command{ Use: "browse [owner/repo]", Short: "Open a repository in your browser", Long: `Open a repository's web page. With no argument the repo is inferred from the current directory's git remote. Use --print to only print the URL.`, Args: cobra.RangeArgs(0, 1), RunE: runBrowse, } browseCmd.Flags().BoolVarP(&browsePrintOnly, "print", "p", false, "print the URL instead of opening it") rootCmd.AddCommand(browseCmd) } func runBrowse(cmd *cobra.Command, args []string) error { cfg, err := loadConfig() if err != nil { return err } spec := "" if len(args) == 1 { spec = args[0] } owner, repo, err := resolveRepo(spec) if err != nil { return err } url := fmt.Sprintf("%s/%s/%s", hostFor(cfg), owner, repo) if browsePrintOnly { fmt.Fprintln(cmd.OutOrStdout(), url) return nil } fmt.Fprintf(cmd.OutOrStdout(), "Opening %s\n", url) return openBrowser(url) } // checkBrowserURL rejects anything the platform opener should not be handed. // The opener will launch whatever handler is registered for a scheme, so a URL // that came from a server (or a stale config) must be a plain web address // before we exec it. func checkBrowserURL(raw string) error { u, err := neturl.Parse(raw) if err != nil { return fmt.Errorf("not a valid URL") } switch strings.ToLower(u.Scheme) { case "http", "https": default: if u.Scheme == "" { return fmt.Errorf("URL has no scheme; only http and https are opened") } return fmt.Errorf("refusing to open a %q URL; only http and https are opened", u.Scheme) } if u.Host == "" { return fmt.Errorf("URL has no host") } return nil } func openBrowser(url string) error { if err := checkBrowserURL(url); err != nil { return err } var name string var args []string switch runtime.GOOS { case "darwin": name = "open" args = []string{url} case "windows": name = "rundll32" args = []string{"url.dll,FileProtocolHandler", url} default: name = "xdg-open" args = []string{url} } return exec.Command(name, args...).Start() }