Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mogensen committed Aug 12, 2020
0 parents commit d501227
Show file tree
Hide file tree
Showing 7 changed files with 464 additions and 0 deletions.
171 changes: 171 additions & 0 deletions cmd/git-goopen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package main

import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"

"github.com/go-git/go-git/v5"
"github.com/kevinburke/ssh_config"
"github.com/mogensen/go-git-open/internal/gitupstreams"
gurl "github.com/whilp/git-urls"

"net/url"
)

func main() {
gitRepo, err := git.PlainOpen(".")
if err != nil {
log.Fatal(err)
}

url, err := getURLFromGitRepo(gitRepo)
if err != nil {
log.Fatal(err)
}

openbrowser(url)
}

func getURLFromGitRepo(gitRepo *git.Repository) (string, error) {
list, err := gitRepo.Remotes()
if err != nil {
log.Fatal(err)
}

for _, r := range list {

// if domain is set in git options we override with this
domain := getOverwriteDomain(gitRepo)
branch := ""

h, err := gitRepo.Head()
if err != nil {
return "", err
}
if h.Name().IsBranch() {
branch = h.Name().Short()
}

url, err := getBrowerURL(r.Config().URLs[0], domain, branch)
if err != nil {
return "", err
}

return url, nil
}

return "", fmt.Errorf("No remote url found")
}

func getBrowerURL(remoteURL string, domain, branch string) (string, error) {
url, err := getURL(remoteURL)
if err != nil {
return "", err
}

f, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "config"))
if err != nil {
return "", err
}
cfg, err := ssh_config.Decode(f)
if err != nil {
return "", err
}
sshConfigDomain, _ := cfg.Get(url.Host, "HostName")

if sshConfigDomain != "" && domain == "" {
domain = sshConfigDomain
}

if strings.Contains(url.Host, "bitbucket.org") {
url, err = gitupstreams.BitbucketOrgURL(url, branch)
if err != nil {
return "", err
}
} else if strings.Contains(url.Host, "azure.com") {
url, err = gitupstreams.AzureURL(url, branch)
if err != nil {
return "", err
}
} else {
url, err = gitupstreams.GenericURL(url, branch)
if err != nil {
return "", err
}
}

fmt.Println("----")
fmt.Printf(" - branch: %s\n", branch)
fmt.Printf(" - domain: %s\n", domain)

if domain != "" {
url.Host = domain
}
fmt.Printf("%s\n", url)

fmt.Println(remoteURL)
return url.String(), nil
}

func getOverwriteDomain(gitRepo *git.Repository) string {

conf, err := gitRepo.Config()
if err != nil {
panic(err)
}

sections := conf.Raw.Sections

for _, s := range sections {
if s.Name == "open" {
return s.Options.Get("domain")
}
}

return ""
}

func getURL(remote string) (*url.URL, error) {

u, err := gurl.Parse(remote)
if err != nil {
return nil, err
}

browserURL := url.URL{
Scheme: "https",
Host: u.Host,
Path: strings.TrimSuffix(u.Path, ".git"),
}

// If the URL is provided as "http", preserve that
if u.Scheme == "http" {
browserURL.Scheme = "http"
}

return &browserURL, nil
}

func openbrowser(url string) {
var err error

switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", url).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
err = exec.Command("open", url).Start()
default:
err = fmt.Errorf("unsupported platform")
}
if err != nil {
log.Fatal(err)
}

}
139 changes: 139 additions & 0 deletions cmd/git-goopen/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package main

import (
"log"
"reflect"
"testing"

"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/storage/memory"
)

func Test_getBrowerURL(t *testing.T) {
tests := []struct {
name string
branch string
remoteURL string
domainOverwrite string
want string
wantErr bool
}{
{
name: "gh: basic",
branch: "master",
remoteURL: "git@github.com:user/repo.git",
want: "https://github.com/user/repo",
wantErr: false,
},
{
name: "gh: basic with branch",
branch: "develop",
remoteURL: "git@github.com:user/repo.git",
want: "https://github.com/user/repo/tree/develop",
wantErr: false,
},
{
name: "gh: basic http",
branch: "master",
remoteURL: "http://github.com/user/repo.git",
want: "http://github.com/user/repo",
wantErr: false,
},
{
name: "gh: basic with domain overwrite",
branch: "master",
remoteURL: "git@github.com:user/repo.git",
domainOverwrite: "repo.git.com",
want: "https://repo.git.com/user/repo",
wantErr: false,
},
{
name: "azure devops: basic",
branch: "master",
remoteURL: "https://CORP@dev.azure.com/v3/CORP/Project/GitRepo",
want: "https://dev.azure.com/CORP/Project/_git/GitRepo",
wantErr: false,
},
{
name: "azure devops: ssh",
branch: "master",
remoteURL: "git@ssh.dev.azure.com:v3/CORP/Project/GitRepo",
want: "https://dev.azure.com/CORP/Project/_git/GitRepo",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getBrowerURL(tt.remoteURL, tt.domainOverwrite, tt.branch)
if (err != nil) != tt.wantErr {
t.Errorf("getBrowerURL() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("getBrowerURL() = %v, want %v", got, tt.want)
}
})
}
}

func Test_getURLFromGitRepo(t *testing.T) {
type args struct {
gitRemote string
gitBranch string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "gh: basic",
args: args{
gitRemote: "git@github.com:git-fixtures/basic.git",
gitBranch: "master",
},
want: "https://github.com/git-fixtures/basic",
wantErr: false,
},
{
name: "gh: basic",
args: args{
gitRemote: "git@github.com:git-fixtures/basic.git",
gitBranch: "branch",
},
want: "https://github.com/git-fixtures/basic/tree/branch",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

fs := memfs.New()
// Git objects storer based on memory
storer := memory.NewStorage()

// Clones the repository into the worktree (fs) and storer all the .git
// content into the storer
gitRepo, err := git.Clone(storer, fs, &git.CloneOptions{
URL: tt.args.gitRemote,
SingleBranch: true,
ReferenceName: plumbing.NewBranchReferenceName(tt.args.gitBranch),
})
if err != nil {
log.Fatal(err)
}

got, err := getURLFromGitRepo(gitRepo)
if (err != nil) != tt.wantErr {
t.Errorf("getURLFromGitRepo() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("getURLFromGitRepo() = %v, want %v", got, tt.want)
}
})
}
}
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/mogensen/go-git-open

go 1.14

require (
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.1.0
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd
github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0
)
Loading

0 comments on commit d501227

Please sign in to comment.