Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate browser package from gh #62

Merged
merged 2 commits into from Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Expand Up @@ -4,8 +4,10 @@ go 1.16

require (
github.com/MakeNowJust/heredoc v1.0.0
github.com/cli/browser v1.1.0
github.com/cli/safeexec v1.0.0
github.com/cli/shurcooL-graphql v0.0.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/henvic/httpretty v0.0.6
github.com/kr/pretty v0.1.0 // indirect
github.com/stretchr/testify v1.7.0
Expand Down
5 changes: 5 additions & 0 deletions go.sum
@@ -1,11 +1,15 @@
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
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/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/cli/shurcooL-graphql v0.0.1 h1:/9J3t9O6p1B8zdBBtQighq5g7DQRItBwuwGh3SocsKM=
github.com/cli/shurcooL-graphql v0.0.1/go.mod h1:U7gCSuMZP/Qy7kbqkk5PrqXEeDgtfG5K+W+u8weorps=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs=
Expand All @@ -27,6 +31,7 @@ github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c=
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
80 changes: 80 additions & 0 deletions pkg/browser/browser.go
@@ -0,0 +1,80 @@
// Package browser facilitates opening of URLs in a web browser.
package browser

import (
"io"
"os"
"os/exec"

cliBrowser "github.com/cli/browser"
"github.com/cli/go-gh/pkg/config"
"github.com/cli/safeexec"
"github.com/google/shlex"
)

// Browser represents a web browser that can be used to open up URLs.
type Browser struct {
launcher string
stderr io.Writer
stdout io.Writer
}

// NewBrowser initializes a Browser. If a launcher is not specified
// one is determined based on environment variables or from the
// configuration file.
// The order of precedence for determining a launcher is:
// - Specified launcher;
// - GH_BROWSER environment variable;
// - browser option from configuration file;
// - BROWSER environment variable.
func NewBrowser(launcher string, stdout, stderr io.Writer) Browser {
samcoe marked this conversation as resolved.
Show resolved Hide resolved
if launcher == "" {
launcher = resolveLauncher()
}
b := Browser{
launcher: launcher,
stderr: stderr,
stdout: stdout,
}
return b
}

// Browse opens the launcher and navigates to the specified URL.
func (b *Browser) Browse(url string) error {
return b.browse(url, nil)
}

func (b *Browser) browse(url string, env []string) error {
if b.launcher != "" {
samcoe marked this conversation as resolved.
Show resolved Hide resolved
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
if env != nil {
cmd.Env = env
}
return cmd.Run()
}
return cliBrowser.OpenURL(url)
}

func resolveLauncher() string {
if ghBrowser := os.Getenv("GH_BROWSER"); ghBrowser != "" {
return ghBrowser
}
cfg, err := config.Read()
if err == nil {
if cfgBrowser, _ := cfg.Get([]string{"browser"}); cfgBrowser != "" {
return cfgBrowser
}
}
return os.Getenv("BROWSER")
}
103 changes: 103 additions & 0 deletions pkg/browser/browser_test.go
@@ -0,0 +1,103 @@
package browser

import (
"bytes"
"fmt"
"os"
"testing"

"github.com/cli/go-gh/pkg/config"
"github.com/stretchr/testify/assert"
)

func TestHelperProcess(t *testing.T) {
if os.Getenv("GH_WANT_HELPER_PROCESS") != "1" {
return
}
fmt.Fprintf(os.Stdout, "%v", os.Args[3:])
os.Exit(0)
}

func TestBrowse(t *testing.T) {
launcher := fmt.Sprintf("%q -test.run=TestHelperProcess -- chrome", os.Args[0])
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
b := Browser{launcher: launcher, stdout: stdout, stderr: stderr}
err := b.browse("github.com", []string{"GH_WANT_HELPER_PROCESS=1"})
assert.NoError(t, err)
assert.Equal(t, "[chrome github.com]", stdout.String())
assert.Equal(t, "", stderr.String())
}

func TestResolveLauncher(t *testing.T) {
tests := []struct {
name string
env map[string]string
config *config.Config
wantLauncher string
}{
{
name: "GH_BROWSER set",
env: map[string]string{
"GH_BROWSER": "GH_BROWSER",
},
wantLauncher: "GH_BROWSER",
},
{
name: "config browser set",
config: config.ReadFromString("browser: CONFIG_BROWSER"),
wantLauncher: "CONFIG_BROWSER",
},
{
name: "BROWSER set",
env: map[string]string{
"BROWSER": "BROWSER",
},
wantLauncher: "BROWSER",
},
{
name: "GH_BROWSER and config browser set",
env: map[string]string{
"GH_BROWSER": "GH_BROWSER",
},
config: config.ReadFromString("browser: CONFIG_BROWSER"),
wantLauncher: "GH_BROWSER",
},
{
name: "config browser and BROWSER set",
env: map[string]string{
"BROWSER": "BROWSER",
},
config: config.ReadFromString("browser: CONFIG_BROWSER"),
wantLauncher: "CONFIG_BROWSER",
},
{
name: "GH_BROWSER and BROWSER set",
env: map[string]string{
"BROWSER": "BROWSER",
"GH_BROWSER": "GH_BROWSER",
},
wantLauncher: "GH_BROWSER",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.env != nil {
for k, v := range tt.env {
old := os.Getenv(k)
os.Setenv(k, v)
defer os.Setenv(k, old)
}
}
if tt.config != nil {
old := config.Read
config.Read = func() (*config.Config, error) {
return tt.config, nil
}
defer func() { config.Read = old }()
}
launcher := resolveLauncher()
assert.Equal(t, tt.wantLauncher, launcher)
})
}
}